diff --git a/.cargo-deny-config.toml b/.cargo-deny-config.toml index b79e85383f..a90029c388 100644 --- a/.cargo-deny-config.toml +++ b/.cargo-deny-config.toml @@ -35,6 +35,20 @@ name = "ring" expression = "MIT AND ISC AND OpenSSL" license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] +[[licenses.clarify]] +name = "webpki" +expression = "ISC" +license-files = [ + { path = "LICENSE", hash = 0x001c7e6c }, +] + +[[licenses.clarify]] +name = "rustls-webpki" +expression = "ISC" +license-files = [ + { path = "LICENSE", hash = 0x001c7e6c }, +] + # This section is considered when running `cargo deny check bans`. # More documentation about the 'bans' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html diff --git a/.cargo/config.toml b/.cargo/config.toml index 0015055659..5afa0e9471 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,7 @@ [build] # Share one `target` directory at the project root for all Cargo projects and workspaces in smithy-rs target-dir = "target" + +# TODO(https://github.com/awslabs/smithy-rs/issues/2766): The sparse registry config can be removed when upgrading to Rust 1.70 +[registries.crates-io] +protocol = "sparse" diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 2229148de3..6f361c926d 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -32,7 +32,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Acquire credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions diff --git a/.github/workflows/ci-merge-queue.yml b/.github/workflows/ci-merge-queue.yml index bedab9beb5..cfad5aab2f 100644 --- a/.github/workflows/ci-merge-queue.yml +++ b/.github/workflows/ci-merge-queue.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Attempt to load a docker login password - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions @@ -67,7 +67,7 @@ jobs: DOCKER_BUILDKIT: 1 run: ./smithy-rs/.github/scripts/acquire-build-image - name: Acquire credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 374d23a9b4..89d8099519 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Attempt to load a docker login password - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions @@ -69,7 +69,7 @@ jobs: DOCKER_BUILDKIT: 1 run: ./smithy-rs/.github/scripts/acquire-build-image - name: Acquire credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions @@ -107,3 +107,41 @@ jobs: secrets: SMITHY_RS_PULL_REQUEST_CDN_S3_BUCKET_NAME: ${{ secrets.SMITHY_RS_PULL_REQUEST_CDN_S3_BUCKET_NAME }} SMITHY_RS_PULL_REQUEST_CDN_ROLE_ARN: ${{ secrets.SMITHY_RS_PULL_REQUEST_CDN_ROLE_ARN }} + + semver-checks: + name: check the semver status of this PR + runs-on: smithy_ubuntu-latest_8-core + needs: + - save-docker-login-token + - acquire-base-image + steps: + - uses: actions/checkout@v3 + with: + path: smithy-rs + ref: ${{ inputs.git_ref }} + - name: Get PR info + id: check-breaking-label + uses: actions/github-script@v6 + with: + script: | + const response = await github.rest.pulls.get({ + pull_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + const labels = response.data.labels.map(l => l.name); + const isBreaking = labels.includes("breaking-change"); + const data = { + labels, + isBreaking + }; + console.log("data:", data); + return data; + - name: Run semver check + uses: ./smithy-rs/.github/actions/docker-build + with: + action: check-semver + action-arguments: ${{ github.event.pull_request.base.sha }} ${{ fromJSON(steps.check-breaking-label.outputs.result).isBreaking }} + - name: print help message + if: failure() + run: echo "::error::This pull request contains breaking changes. Please add the `breaking-changes` label and a changelog entry" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8814fdd364..f4208b271a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ on: required: false env: - rust_version: 1.66.1 + rust_version: 1.69.0 rust_toolchain_components: clippy,rustfmt ENCRYPTED_DOCKER_PASSWORD: ${{ secrets.ENCRYPTED_DOCKER_PASSWORD }} DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} @@ -84,6 +84,9 @@ jobs: test: - action: check-aws-sdk-adhoc-tests runner: ubuntu-latest + # TODO(enableNewSmithyRuntimeCleanup): Remove `check-aws-sdk-middleware-impl` when cleaning up middleware + - action: check-aws-sdk-middleware-impl + runner: smithy_ubuntu-latest_8-core - action: check-client-codegen-integration-tests runner: smithy_ubuntu-latest_8-core - action: check-client-codegen-unit-tests @@ -108,6 +111,8 @@ jobs: runner: ubuntu-latest - action: check-style-and-lints runner: ubuntu-latest + - action: check-book + runner: ubuntu-latest - action: check-tools runner: smithy_ubuntu-latest_8-core - action: check-deterministic-codegen @@ -159,6 +164,8 @@ jobs: with: action: ${{ matrix.test.action }} + + test-rust-windows: name: Rust Tests on Windows runs-on: windows-latest @@ -189,8 +196,9 @@ jobs: pushd "${runtime_path}" &>/dev/null # aws-smithy-http-server-python cannot be compiled on Windows since it uses the `signal-hook` crate # which is not really yet fully supported on the platform. - cargo test --all-features --workspace --exclude aws-smithy-http-server-python - cargo doc --no-deps --document-private-items --all-features --workspace --exclude aws-smithy-http-server-python + # aws-smithy-http-server-typescript cannot be compiled right now on Windows. + cargo test --all-features --workspace --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript + cargo doc --no-deps --document-private-items --all-features --workspace --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript popd &>/dev/null done @@ -205,29 +213,27 @@ jobs: matrix: include: # We always exclude aws-smithy-http-server-python since the Python framework is experimental. - # We only build the `native-tls` feature here because `rustls` depends on `ring` which in turn - # does not support powerpc as a target platform (see https://github.com/briansmith/ring/issues/389) - target: i686-unknown-linux-gnu build_smithy_rs_features: --all-features build_aws_exclude: '' - build_smithy_rs_exclude: --exclude aws-smithy-http-server-python + build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript test_smithy_rs_features: --all-features test_aws_exclude: '' - test_smithy_rs_exclude: --exclude aws-smithy-http-server-python + test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript - target: powerpc-unknown-linux-gnu - build_smithy_rs_features: --features native-tls + build_smithy_rs_features: '' build_aws_exclude: --exclude aws-inlineable - build_smithy_rs_exclude: --exclude aws-smithy-http-server-python - test_smithy_rs_features: --features native-tls + build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript + test_smithy_rs_features: '' test_aws_exclude: --exclude aws-inlineable - test_smithy_rs_exclude: --exclude aws-smithy-http-server-python + test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript - target: powerpc64-unknown-linux-gnu - build_smithy_rs_features: --features native-tls + build_smithy_rs_features: '' build_aws_exclude: --exclude aws-inlineable - build_smithy_rs_exclude: --exclude aws-smithy-http-server-python - test_smithy_rs_features: --features native-tls + build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript + test_smithy_rs_features: '' test_aws_exclude: --exclude aws-inlineable - test_smithy_rs_exclude: --exclude aws-smithy-http-server-python + test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript env: CROSS_CONFIG: Cross.toml steps: diff --git a/.github/workflows/claim-crate-names.yml b/.github/workflows/claim-crate-names.yml index ef24446bb7..6f714a7671 100644 --- a/.github/workflows/claim-crate-names.yml +++ b/.github/workflows/claim-crate-names.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.66.1 + rust_version: 1.69.0 name: Claim unpublished crate names on crates.io run-name: ${{ github.workflow }} diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 83926c09ac..c15767baa1 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -7,6 +7,9 @@ on: name: Update GitHub Pages +env: + rust_version: 1.69.0 + # Allow only one doc pages build to run at a time for the entire smithy-rs repo concurrency: group: github-pages-yml @@ -20,7 +23,9 @@ jobs: uses: actions/checkout@v3 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.rust_version }} - name: Generate docs env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index 6d22213eb5..82fc80d975 100644 --- a/.github/workflows/pull-request-bot.yml +++ b/.github/workflows/pull-request-bot.yml @@ -28,7 +28,7 @@ concurrency: env: java_version: 11 - rust_version: 1.66.1 + rust_version: 1.69.0 rust_toolchain_components: clippy,rustfmt apt_dependencies: libssl-dev gnuplot jq @@ -55,7 +55,7 @@ jobs: with: action: generate-codegen-diff action-arguments: ${{ inputs.base_revision }} - - uses: aws-actions/configure-aws-credentials@v1-node16 + - uses: aws-actions/configure-aws-credentials@v2.2.0 name: Acquire credentials for uploading to S3 with: role-to-assume: ${{ secrets.SMITHY_RS_PULL_REQUEST_CDN_ROLE_ARN }} @@ -110,7 +110,7 @@ jobs: ./gradlew -Paws.services=+sts,+sso,+transcribestreaming,+dynamodb :aws:sdk:assemble # Copy the Server runtime crate(s) in - cp -r rust-runtime/aws-smithy-http-server rust-runtime/aws-smithy-http-server-python aws/sdk/build/aws-sdk/sdk + cp -r rust-runtime/aws-smithy-http-server rust-runtime/aws-smithy-http-server-python rust-runtime/aws-smithy-http-server-typescript aws/sdk/build/aws-sdk/sdk pushd aws/sdk/build/aws-sdk @@ -118,14 +118,14 @@ jobs: sed -i '/examples/d' Cargo.toml # Add server runtime crates to the workspace - sed -i 's/"sdk\/sts",/"sdk\/sts","sdk\/aws-smithy-http-server","sdk\/aws-smithy-http-server-python",/' Cargo.toml + sed -i 's/"sdk\/sts",/"sdk\/sts","sdk\/aws-smithy-http-server","sdk\/aws-smithy-http-server-python","sdk\/aws-smithy-http-server-typescript",/' Cargo.toml cargo doc --no-deps --all-features popd ./tools/ci-scripts/generate-doc-preview-index.sh ${{ inputs.base_revision }} echo 'bot-message=A [new doc preview](https://d2luzm2xt3nokh.cloudfront.net/docs/'${{ inputs.head_revision }}'/index.html) is ready to view.' >> "${GITHUB_OUTPUT}" - - uses: aws-actions/configure-aws-credentials@v1-node16 + - uses: aws-actions/configure-aws-credentials@v2.2.0 name: Acquire credentials for uploading to S3 with: role-to-assume: ${{ secrets.SMITHY_RS_PULL_REQUEST_CDN_ROLE_ARN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52ddfdfe7a..7d33ceba4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.66.1 + rust_version: 1.69.0 name: Release smithy-rs run-name: ${{ github.workflow }} ${{ inputs.semantic_version }} (${{ inputs.commit_sha }}) - ${{ inputs.dry_run && 'Dry run' || 'Production run' }} diff --git a/.github/workflows/update-sdk-next.yml b/.github/workflows/update-sdk-next.yml index c96b09b73f..155924020a 100644 --- a/.github/workflows/update-sdk-next.yml +++ b/.github/workflows/update-sdk-next.yml @@ -32,7 +32,7 @@ jobs: - name: Set up Rust uses: dtolnay/rust-toolchain@master with: - toolchain: 1.66.1 + toolchain: 1.69.0 - name: Delete old SDK run: | - name: Generate a fresh SDK @@ -47,7 +47,7 @@ jobs: git checkout origin/main -b next # Delete the old SDK - rm -rf sdk examples + rm -rf sdk examples tests rm -f versions.toml Cargo.toml index.md # Copy in the new SDK diff --git a/.gitignore b/.gitignore index 710a23e16b..268344ecb1 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,6 @@ target/ # tools .tool-versions + +# python +__pycache__ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cce6e26327..a45d403a09 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: files: ^.*$ pass_filenames: false - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.6.0 + rev: v2.10.0 hooks: - id: pretty-format-kotlin args: [--autofix, --ktlint-version, 0.48.2] diff --git a/CHANGELOG.md b/CHANGELOG.md index e4eaa57e2f..9cb6e47746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,496 @@ +August 1st, 2023 +================ +**Breaking Changes:** +- ⚠🎉 (server, [smithy-rs#2740](https://github.com/awslabs/smithy-rs/issues/2740), [smithy-rs#2759](https://github.com/awslabs/smithy-rs/issues/2759), [smithy-rs#2779](https://github.com/awslabs/smithy-rs/issues/2779), [smithy-rs#2827](https://github.com/awslabs/smithy-rs/issues/2827), @hlbarber) The middleware system has been reworked as we push for a unified, simple, and consistent API. The following changes have been made in service of this goal: + + - A `ServiceShape` trait has been added. + - The `Plugin` trait has been simplified. + - The `HttpMarker` and `ModelMarker` marker traits have been added to better distinguish when plugins run and what they have access to. + - The `Operation` structure has been removed. + - A `Scoped` `Plugin` has been added. + + The `Plugin` trait has now been simplified and the `Operation` struct has been removed. + + ## Addition of `ServiceShape` + + Since the [0.52 release](https://github.com/awslabs/smithy-rs/releases/tag/release-2022-12-12) the `OperationShape` has existed. + + ```rust + /// Models the [Smithy Operation shape]. + /// + /// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation + pub trait OperationShape { + /// The ID of the operation. + const ID: ShapeId; + + /// The operation input. + type Input; + /// The operation output. + type Output; + /// The operation error. [`Infallible`](std::convert::Infallible) in the case where no error + /// exists. + type Error; + } + ``` + + This allowed `Plugin` authors to access these associated types and constants. See the [`PrintPlugin`](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs) as an example. + + We continue with this approach and introduce the following trait: + + ```rust + /// Models the [Smithy Service shape]. + /// + /// [Smithy Service shape]: https://smithy.io/2.0/spec/service-types.html + pub trait ServiceShape { + /// The [`ShapeId`] of the service. + const ID: ShapeId; + + /// The version of the service. + const VERSION: Option<&'static str>; + + /// The [Protocol] applied to this service. + /// + /// [Protocol]: https://smithy.io/2.0/spec/protocol-traits.html + type Protocol; + + /// An enumeration of all operations contained in this service. + type Operations; + } + ``` + + With the changes to `Plugin`, described below, middleware authors now have access to this information at compile time. + + ## Simplication of the `Plugin` trait + + Previously, + + ```rust + trait Plugin { + type Service; + type Layer; + + fn map(&self, input: Operation) -> Operation; + } + ``` + + modified an `Operation`. + + Now, + + ```rust + trait Plugin { + type Output; + + fn apply(&self, input: T) -> Self::Output; + } + ``` + + maps a `tower::Service` to a `tower::Service`. This is equivalent to `tower::Layer` with two extra type parameters: `Service` and `Operation`, which implement `ServiceShape` and `OperationShape` respectively. + + Having both `Service` and `Operation` as type parameters also provides an even surface for advanced users to extend the codegenerator in a structured way. See [this issue](https://github.com/awslabs/smithy-rs/issues/2777) for more context. + + The following middleware setup + + ```rust + pub struct PrintService { + inner: S, + name: &'static str, + } + + impl Service for PrintService + where + S: Service, + { + async fn call(&mut self, req: R) -> Self::Future { + println!("Hi {}", self.name); + self.inner.call(req) + } + } + + pub struct PrintLayer { + name: &'static str, + } + + impl Layer for PrintLayer { + type Service = PrintService; + + fn layer(&self, service: S) -> Self::Service { + PrintService { + inner: service, + name: self.name, + } + } + } + + pub struct PrintPlugin; + + impl Plugin for PrintPlugin + where + Op: OperationShape, + { + type Service = S; + type Layer = Stack; + + fn map(&self, input: Operation) -> Operation { + input.layer(PrintLayer { name: Op::NAME }) + } + } + ``` + + now becomes + + ```rust + pub struct PrintService { + inner: S, + name: &'static str, + } + + impl Service for PrintService + where + S: Service, + { + async fn call(&mut self, req: R) -> Self::Future { + println!("Hi {}", self.name); + self.inner.call(req) + } + } + + pub struct PrintPlugin; + + impl Plugin for PrintPlugin + where + Op: OperationShape, + { + type Output = PrintService; + + fn apply(&self, inner: T) -> Self::Output { + PrintService { inner, name: Op::ID.name() } + } + } + + impl HttpMarker for PrintPlugin { } + ``` + + Alternatively, using the new `ServiceShape`, implemented on `Ser`: + + ```rust + impl Plugin for PrintPlugin + where + Ser: ServiceShape, + { + type Service = PrintService; + + fn apply(&self, inner: T) -> Self::Service { + PrintService { inner, name: Ser::ID.name() } + } + } + ``` + + A single `Plugin` can no longer apply a `tower::Layer` on HTTP requests/responses _and_ modelled structures at the same time (see middleware positions [C](https://awslabs.github.io/smithy-rs/design/server/middleware.html#c-operation-specific-http-middleware) and [D](https://awslabs.github.io/smithy-rs/design/server/middleware.html#d-operation-specific-model-middleware). Instead one `Plugin` must be specified for each and passed to the service builder constructor separately: + + ```rust + let app = PokemonService::builder_with_plugins(/* HTTP plugins */, /* model plugins */) + /* setters */ + .build() + .unwrap(); + ``` + + To better distinguish when a plugin runs and what it has access to, `Plugin`s now have to additionally implement the `HttpMarker` marker trait, the `ModelMarker` marker trait, or both: + + - A HTTP plugin acts on the HTTP request before it is deserialized, and acts on the HTTP response after it is serialized. + - A model plugin acts on the modeled operation input after it is deserialized, and acts on the modeled operation output or the modeled operation error before it is serialized. + + The motivation behind this change is to simplify the job of middleware authors, separate concerns, accomodate common cases better, and to improve composition internally. + + Because `Plugin` is now closer to `tower::Layer` we have two canonical converters: + + ```rust + use aws_smithy_http_server::plugin::{PluginLayer, LayerPlugin}; + + // Convert from `Layer` to `Plugin` which applies uniformly across all operations + let layer = /* some layer */; + let plugin = PluginLayer(layer); + + // Convert from `Plugin` to `Layer` for some fixed protocol and operation + let plugin = /* some plugin */; + let layer = LayerPlugin::new::(plugin); + ``` + + ## Removal of `PluginPipeline` + + Since plugins now come in two flavors (those marked with `HttpMarker` and those marked with `ModelMarker`) that shouldn't be mixed in a collection of plugins, the primary way of concatenating plugins, `PluginPipeline` has been removed in favor of the `HttpPlugins` and `ModelPlugins` types, which eagerly check that whenever a plugin is pushed, it is of the expected type. + + This worked before, but you wouldn't be able to do apply this collection of plugins anywhere; if you tried to, the compilation error messages would not be very helpful: + + ```rust + use aws_smithy_http_server::plugin::PluginPipeline; + + let pipeline = PluginPipeline::new().push(http_plugin).push(model_plugin); + ``` + + Now collections of plugins must contain plugins of the same flavor: + + ```rust + use aws_smithy_http_server::plugin::{HttpPlugins, ModelPlugins}; + + let http_plugins = HttpPlugins::new() + .push(http_plugin) + // .push(model_plugin) // This fails to compile with a helpful error message. + .push(&http_and_model_plugin); + let model_plugins = ModelPlugins::new() + .push(model_plugin) + .push(&http_and_model_plugin); + ``` + + In the above example, `&http_and_model_plugin` implements both `HttpMarker` and `ModelMarker`, so we can add it to both collections. + + ## Removal of `Operation` + + The `aws_smithy_http_server::operation::Operation` structure has now been removed. Previously, there existed a `{operation_name}_operation` setter on the service builder, which accepted an `Operation`. This allowed users to + + ```rust + let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_service(/* tower::Service */); + + let app = PokemonService::builder_without_plugins() + .get_pokemon_species_operation(operation) + /* other setters */ + .build() + .unwrap(); + ``` + + to set an operation with a `tower::Service`, and + + ```rust + let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_service(/* tower::Service */).layer(/* layer */); + let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_handler(/* closure */).layer(/* layer */); + + let app = PokemonService::builder_without_plugins() + .get_pokemon_species_operation(operation) + /* other setters */ + .build() + .unwrap(); + ``` + + to add a `tower::Layer` (acting on HTTP requests/responses post-routing) to a single operation. + + We have seen little adoption of this API and for this reason we have opted instead to introduce a new setter, accepting a `tower::Service`, on the service builder: + + ```rust + let app = PokemonService::builder_without_plugins() + .get_pokemon_species_service(/* tower::Service */) + /* other setters */ + .build() + .unwrap(); + ``` + + Applying a `tower::Layer` to a _subset_ of operations is should now be done through the `Plugin` API via `filter_by_operation_id` + + ```rust + use aws_smithy_http_server::plugin::{PluginLayer, filter_by_operation_name, IdentityPlugin}; + + let plugin = PluginLayer(/* layer */); + let scoped_plugin = filter_by_operation_name(plugin, |id| id == GetPokemonSpecies::ID); + + let app = PokemonService::builder_with_plugins(scoped_plugin, IdentityPlugin) + .get_pokemon_species(/* handler */) + /* other setters */ + .build() + .unwrap(); + ``` + + or the new `Scoped` `Plugin` introduced below. + + # Addition of `Scoped` + + Currently, users can selectively apply a `Plugin` via the `filter_by_operation_id` function + + ```rust + use aws_smithy_http_server::plugin::filter_by_operation_id; + // Only apply `plugin` to `CheckHealth` and `GetStorage` operation + let filtered_plugin = filter_by_operation_id(plugin, |name| name == CheckHealth::ID || name == GetStorage::ID); + ``` + + In addition to this, we now provide `Scoped`, which selectively applies a `Plugin` at _compiletime_. Users should prefer this to `filter_by_operation_id` when applicable. + + ```rust + use aws_smithy_http_server::plugin::Scoped; + use pokemon_service_server_sdk::scoped; + + scope! { + /// Includes only the `CheckHealth` and `GetStorage` operation. + struct SomeScope { + includes: [CheckHealth, GetStorage] + } + } + let scoped_plugin = Scoped::new::(plugin); + ``` + +- ⚠ (all, [smithy-rs#2675](https://github.com/awslabs/smithy-rs/issues/2675)) Remove native-tls and add a migration guide. +- ⚠ (client, [smithy-rs#2671](https://github.com/awslabs/smithy-rs/issues/2671))
+ Breaking change in how event stream signing works (click to expand more details) + + This change will only impact you if you are wiring up their own event stream signing/authentication scheme. If you're using `aws-sig-auth` to use AWS SigV4 event stream signing, then this change will **not** impact you. + + Previously, event stream signing was configured at codegen time by placing a `new_event_stream_signer` method on the `Config`. This function was called at serialization time to connect the signer to the streaming body. Now, instead, a special `DeferredSigner` is wired up at serialization time that relies on a signing implementation to be sent on a channel by the HTTP request signer. To do this, a `DeferredSignerSender` must be pulled out of the property bag, and its `send()` method called with the desired event stream signing implementation. + + See the changes in https://github.com/awslabs/smithy-rs/pull/2671 for an example of how this was done for SigV4. +
+- ⚠ (all, [smithy-rs#2673](https://github.com/awslabs/smithy-rs/issues/2673)) For event stream operations, the `EventStreamSender` in inputs/outputs now requires the passed in `Stream` impl to implement `Sync`. +- ⚠ (server, [smithy-rs#2539](https://github.com/awslabs/smithy-rs/issues/2539)) Code generation will abort if the `ignoreUnsupportedConstraints` codegen flag has no effect, that is, if all constraint traits used in your model are well-supported. Please remove the flag in such case. +- ⚠ (client, [smithy-rs#2728](https://github.com/awslabs/smithy-rs/issues/2728), [smithy-rs#2262](https://github.com/awslabs/smithy-rs/issues/2262), [aws-sdk-rust#2087](https://github.com/awslabs/aws-sdk-rust/issues/2087)) The property bag type for Time is now `SharedTimeSource`, not `SystemTime`. If your code relies on setting request time, use `aws_smithy_async::time::SharedTimeSource`. +- ⚠ (server, [smithy-rs#2676](https://github.com/awslabs/smithy-rs/issues/2676), [smithy-rs#2685](https://github.com/awslabs/smithy-rs/issues/2685)) Bump dependency on `lambda_http` by `aws-smithy-http-server` to 0.8.0. This version of `aws-smithy-http-server` is only guaranteed to be compatible with 0.8.0, or semver-compatible versions of 0.8.0 of the `lambda_http` crate. It will not work with versions prior to 0.8.0 _at runtime_, making requests to your smithy-rs service unroutable, so please make sure you're running your service in a compatible configuration +- ⚠ (server, [smithy-rs#2457](https://github.com/awslabs/smithy-rs/issues/2457), @hlbarber) Remove `PollError` from an operations `Service::Error`. + + Any [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) provided to + [`Operation::from_service`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/struct.Operation.html#method.from_service) + no longer requires `Service::Error = OperationError`, instead requiring just `Service::Error = Op::Error`. +- ⚠ (client, [smithy-rs#2742](https://github.com/awslabs/smithy-rs/issues/2742)) A newtype wrapper `SharedAsyncSleep` has been introduced and occurrences of `Arc` that appear in public APIs have been replaced with it. +- ⚠ (all, [smithy-rs#2893](https://github.com/awslabs/smithy-rs/issues/2893)) Update MSRV to Rust 1.69.0 +- ⚠ (server, [smithy-rs#2678](https://github.com/awslabs/smithy-rs/issues/2678)) `ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name. + `OperationExtension`'s members are replaced by the `ShapeId` and operations' names are now replced by a `ShapeId`. + + Before you had an operation and an absolute name as its `NAME` member. You could apply a plugin only to some selected operation: + + ``` + filter_by_operation_name(plugin, |name| name != Op::ID); + ``` + + Your new filter selects on an operation's absolute name, namespace or name. + + ``` + filter_by_operation_id(plugin, |id| id.name() != Op::ID.name()); + ``` + + The above filter is applied to an operation's name, the one you use to specify the operation in the Smithy model. + + You can filter all operations in a namespace or absolute name: + + ``` + filter_by_operation_id(plugin, |id| id.namespace() != "namespace"); + filter_by_operation_id(plugin, |id| id.absolute() != "namespace#name"); + ``` +- ⚠ (client, [smithy-rs#2758](https://github.com/awslabs/smithy-rs/issues/2758)) The occurrences of `Arc` have now been replaced with `SharedEndpointResolver` in public APIs. +- ⚠ (server, [smithy-rs#2740](https://github.com/awslabs/smithy-rs/issues/2740), [smithy-rs#2759](https://github.com/awslabs/smithy-rs/issues/2759), [smithy-rs#2779](https://github.com/awslabs/smithy-rs/issues/2779), @hlbarber) Remove `filter_by_operation_id` and `plugin_from_operation_id_fn` in favour of `filter_by_operation` and `plugin_from_operation_fn`. + + Previously, we provided `filter_by_operation_id` which filtered `Plugin` application via a predicate over the Shape ID. + + ```rust + use aws_smithy_http_server::plugin::filter_by_operation_id; + use pokemon_service_server_sdk::operation_shape::CheckHealth; + + let filtered = filter_by_operation_id(plugin, |name| name != CheckHealth::NAME); + ``` + + This had the problem that the user is unable to exhaustively match over a `&'static str`. To remedy this we have switched to `filter_by_operation` which is a predicate over an enum containing all operations contained in the service. + + ```rust + use aws_smithy_http_server::plugin::filter_by_operation_id; + use pokemon_service_server_sdk::service::Operation; + + let filtered = filter_by_operation(plugin, |op: Operation| op != Operation::CheckHealth); + ``` + + Similarly, `plugin_from_operation_fn` now allows for + + ```rust + use aws_smithy_http_server::plugin::plugin_from_operation_fn; + use pokemon_service_server_sdk::service::Operation; + + fn map(op: Operation, inner: S) -> PrintService { + match op { + Operation::CheckHealth => PrintService { name: op.shape_id().name(), inner }, + Operation::GetPokemonSpecies => PrintService { name: "hello world", inner }, + _ => todo!() + } + } + + let plugin = plugin_from_operation_fn(map); + ``` +- ⚠ (client, [smithy-rs#2783](https://github.com/awslabs/smithy-rs/issues/2783)) The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`. +- ⚠ (client, [smithy-rs#2845](https://github.com/awslabs/smithy-rs/issues/2845)) `aws_smithy_async::future::rendezvous::Sender::send` no longer exposes `tokio::sync::mpsc::error::SendError` for the error of its return type and instead exposes a new-type wrapper called `aws_smithy_async::future::rendezvous::error::SendError`. In addition, the `aws_smithy_xml` crate no longer exposes types from `xmlparser`. +- ⚠ (client, [smithy-rs#2848](https://github.com/awslabs/smithy-rs/issues/2848)) The implementation `From` for `aws_smithy_http::event_stream::RawMessage` has been removed. +- ⚠ (server, [smithy-rs#2865](https://github.com/awslabs/smithy-rs/issues/2865)) The `alb_health_check` module has been moved out of the `plugin` module into a new `layer` module. ALB health checks should be enacted before routing, and plugins run after routing, so the module location was misleading. Examples have been corrected to reflect the intended application of the layer. +- ⚠ (client, [smithy-rs#2873](https://github.com/awslabs/smithy-rs/issues/2873)) The `test-util` feature in aws-smithy-client has been split to include a separate `wiremock` feature. This allows test-util to be used without a Hyper server dependency making it usable in webassembly targets. +- ⚠ (client) The entire architecture of generated clients has been overhauled. See the [upgrade guide](https://github.com/awslabs/smithy-rs/discussions/2887) to get your code working again. + +**New this release:** +- 🎉 (all, [smithy-rs#2647](https://github.com/awslabs/smithy-rs/issues/2647), [smithy-rs#2645](https://github.com/awslabs/smithy-rs/issues/2645), [smithy-rs#2646](https://github.com/awslabs/smithy-rs/issues/2646), [smithy-rs#2616](https://github.com/awslabs/smithy-rs/issues/2616), @thomas-k-cameron) Implement unstable serde support for the `Number`, `Blob`, `Document`, `DateTime` primitives +- 🎉 (client, [smithy-rs#2652](https://github.com/awslabs/smithy-rs/issues/2652), @thomas-k-cameron) Add a `send_with` function on `-Input` types for sending requests without fluent builders +- (client, [smithy-rs#2791](https://github.com/awslabs/smithy-rs/issues/2791), @davidsouther) Add accessors to Builders +- (all, [smithy-rs#2786](https://github.com/awslabs/smithy-rs/issues/2786), @yotamofek) Avoid intermediate vec allocations in AggregatedBytes::to_vec. +- 🐛 (server, [smithy-rs#2733](https://github.com/awslabs/smithy-rs/issues/2733), @thor-bjorgvinsson) Fix bug in AWS JSON 1.x routers where, if a service had more than 14 operations, the router was created without the route for the 15th operation. +- (client, [smithy-rs#2728](https://github.com/awslabs/smithy-rs/issues/2728), [smithy-rs#2262](https://github.com/awslabs/smithy-rs/issues/2262), [aws-sdk-rust#2087](https://github.com/awslabs/aws-sdk-rust/issues/2087)) Time is now controlled by the `TimeSource` trait. This facilitates testing as well as use cases like WASM where `SystemTime::now()` is not supported. +- 🐛 (client, [smithy-rs#2767](https://github.com/awslabs/smithy-rs/issues/2767), @mcmasn-amzn) Fix bug in client generation when using smithy.rules#endpointTests and operation and service shapes are in different namespaces. +- (client, [smithy-rs#2854](https://github.com/awslabs/smithy-rs/issues/2854)) Public fields in structs are no longer marked as `#[doc(hidden)]`, and they are now visible. +- (server, [smithy-rs#2866](https://github.com/awslabs/smithy-rs/issues/2866)) [RestJson1](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html#operation-error-serialization) server SDKs now serialize only the [shape name](https://smithy.io/2.0/spec/model.html#shape-id) in operation error responses. Previously (from versions 0.52.0 to 0.55.4), the full shape ID was rendered. + Example server error response by a smithy-rs server version 0.52.0 until 0.55.4: + ``` + HTTP/1.1 400 Bad Request + content-type: application/json + x-amzn-errortype: com.example.service#InvalidRequestException + ... + ``` + Example server error response now: + ``` + HTTP/1.1 400 Bad Request + content-type: application/json + x-amzn-errortype: InvalidRequestException + ... + ``` + +**Contributors** +Thank you for your contributions! ❤ +- @davidsouther ([smithy-rs#2791](https://github.com/awslabs/smithy-rs/issues/2791)) +- @hlbarber ([smithy-rs#2457](https://github.com/awslabs/smithy-rs/issues/2457), [smithy-rs#2740](https://github.com/awslabs/smithy-rs/issues/2740), [smithy-rs#2759](https://github.com/awslabs/smithy-rs/issues/2759), [smithy-rs#2779](https://github.com/awslabs/smithy-rs/issues/2779), [smithy-rs#2827](https://github.com/awslabs/smithy-rs/issues/2827)) +- @mcmasn-amzn ([smithy-rs#2767](https://github.com/awslabs/smithy-rs/issues/2767)) +- @thomas-k-cameron ([smithy-rs#2616](https://github.com/awslabs/smithy-rs/issues/2616), [smithy-rs#2645](https://github.com/awslabs/smithy-rs/issues/2645), [smithy-rs#2646](https://github.com/awslabs/smithy-rs/issues/2646), [smithy-rs#2647](https://github.com/awslabs/smithy-rs/issues/2647), [smithy-rs#2652](https://github.com/awslabs/smithy-rs/issues/2652)) +- @thor-bjorgvinsson ([smithy-rs#2733](https://github.com/awslabs/smithy-rs/issues/2733)) +- @yotamofek ([smithy-rs#2786](https://github.com/awslabs/smithy-rs/issues/2786)) + + +May 23rd, 2023 +============== +**New this release:** +- (all, [smithy-rs#2612](https://github.com/awslabs/smithy-rs/issues/2612)) The `Debug` implementation for `PropertyBag` now prints a list of the types it contains. This significantly improves debuggability. +- (all, [smithy-rs#2653](https://github.com/awslabs/smithy-rs/issues/2653), [smithy-rs#2656](https://github.com/awslabs/smithy-rs/issues/2656), @henriiik) Implement `Ord` and `PartialOrd` for `DateTime`. +- 🐛 (client, [smithy-rs#2696](https://github.com/awslabs/smithy-rs/issues/2696)) Fix compiler errors in generated code when naming shapes after types in the Rust standard library prelude. + +**Contributors** +Thank you for your contributions! ❤ +- @henriiik ([smithy-rs#2653](https://github.com/awslabs/smithy-rs/issues/2653), [smithy-rs#2656](https://github.com/awslabs/smithy-rs/issues/2656)) + + +April 26th, 2023 +================ +**Breaking Changes:** +- ⚠ (all, [smithy-rs#2611](https://github.com/awslabs/smithy-rs/issues/2611)) Update MSRV to Rust 1.67.1 + +**New this release:** +- 🎉 (server, [smithy-rs#2540](https://github.com/awslabs/smithy-rs/issues/2540)) Implement layer for servers to handle [ALB health checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html). + Take a look at `aws_smithy_http_server::plugin::alb_health_check` to learn about it. +- 🎉 (client, [smithy-rs#2254](https://github.com/awslabs/smithy-rs/issues/2254), @eduardomourar) Clients now compile for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it! +- (server, [smithy-rs#2540](https://github.com/awslabs/smithy-rs/issues/2540)) Implement `PluginPipeline::http_layer` which allows you to apply a `tower::Layer` to all operations. +- (client, [aws-sdk-rust#784](https://github.com/awslabs/aws-sdk-rust/issues/784), @abusch) Implement std::error::Error#source() properly for the service meta Error enum. +- 🐛 (all, [smithy-rs#2496](https://github.com/awslabs/smithy-rs/issues/2496)) The outputs for event stream operations now implement the `Sync` auto-trait. +- 🐛 (all, [smithy-rs#2495](https://github.com/awslabs/smithy-rs/issues/2495)) Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts. +- 🐛 (client, [smithy-rs#2495](https://github.com/awslabs/smithy-rs/issues/2495)) Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts. +- (client, [smithy-rs#2507](https://github.com/awslabs/smithy-rs/issues/2507)) The `enableNewCrateOrganizationScheme` codegen flag has been removed. If you opted out of the new crate organization scheme, it must be adopted now in order to upgrade (see [the upgrade guidance](https://github.com/awslabs/smithy-rs/discussions/2449) from March 23rd's release). +- (client, [smithy-rs#2534](https://github.com/awslabs/smithy-rs/issues/2534)) `aws_smithy_types::date_time::Format` has been re-exported in service client crates. +- 🐛 (server, [smithy-rs#2582](https://github.com/awslabs/smithy-rs/issues/2582), [smithy-rs#2585](https://github.com/awslabs/smithy-rs/issues/2585)) Fix generation of constrained shapes reaching `@sensitive` shapes +- 🐛 (server, [smithy-rs#2583](https://github.com/awslabs/smithy-rs/issues/2583), [smithy-rs#2584](https://github.com/awslabs/smithy-rs/issues/2584)) Fix server code generation bug affecting constrained shapes bound with `@httpPayload` +- (client, [smithy-rs#2603](https://github.com/awslabs/smithy-rs/issues/2603)) Add a sensitive method to `ParseHttpResponse`. When this returns true, logging of the HTTP response body will be suppressed. + +**Contributors** +Thank you for your contributions! ❤ +- @abusch ([aws-sdk-rust#784](https://github.com/awslabs/aws-sdk-rust/issues/784)) +- @eduardomourar ([smithy-rs#2254](https://github.com/awslabs/smithy-rs/issues/2254)) + + +April 11th, 2023 +================ + + March 23rd, 2023 ================ **Breaking Changes:** diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7770e39605..fc4c4c2578 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -9,74 +9,4 @@ # message = "Fix typos in module documentation for generated crates" # references = ["smithy-rs#920"] # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} -# author = "rcoh" - -[[aws-sdk-rust]] -message = "The outputs for event stream operations (for example, S3's SelectObjectContent) now implement the `Sync` auto-trait." -references = ["smithy-rs#2496"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "jdisanti" - -[[smithy-rs]] -message = "The outputs for event stream operations now implement the `Sync` auto-trait." -references = ["smithy-rs#2496"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "all"} -author = "jdisanti" - -[[aws-sdk-rust]] -message = "The AWS SDK now compiles for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!" -references = ["smithy-rs#2254"] -meta = { "breaking" = false, "tada" = true, "bug" = false } -author = "eduardomourar" - -[[smithy-rs]] -message = "Clients now compile for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!" -references = ["smithy-rs#2254"] -meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "client"} -author = "eduardomourar" - -[[smithy-rs]] -message = "Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts." -references = ["smithy-rs#2495"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "jdisanti" - -[[smithy-rs]] -message = "Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts." -references = ["smithy-rs#2495"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client"} -author = "jdisanti" - -[[smithy-rs]] -message = "The `enableNewCrateOrganizationScheme` codegen flag has been removed. If you opted out of the new crate organization scheme, it must be adopted now in order to upgrade (see [the upgrade guidance](https://github.com/awslabs/smithy-rs/discussions/2449) from March 23rd's release)." -references = ["smithy-rs#2507"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } -author = "jdisanti" - -[[aws-sdk-rust]] -message = """ -S3's `GetObject` will no longer panic when checksum validation is enabled and the target object was uploaded as a multi-part upload. -However, these objects cannot be checksum validated by the SDK due to the way checksums are calculated for multipart uploads. -For more information, see [this page](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums). -""" -references = ["aws-sdk-rust#764"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "Velfi" - -[[aws-sdk-rust]] -message = "`AppName` is now configurable from within `ConfigLoader`." -references = ["smithy-rs#2513"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "ysaito1001" - -[[aws-sdk-rust]] -message = "Add support for omitting session token in canonical requests for SigV4 signing." -references = ["smithy-rs#2473"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "martinjlowm" - -[[aws-sdk-rust]] -message = "Add `into_segments` method to `AggregatedBytes`, for zero-copy conversions." -references = ["smithy-rs#2525"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "parker-timmerman" +# author = "rcoh" \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS index 9671079212..79b21893c0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,34 +1,40 @@ -* @awslabs/rust-sdk-owners +* @awslabs/rust-sdk-owners # Server -/codegen-server-test/ @awslabs/smithy-rs-server -/codegen-server/ @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-http-server/ @awslabs/smithy-rs-server +/codegen-server-test/ @awslabs/smithy-rs-server +/codegen-server/ @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-http-server/ @awslabs/smithy-rs-server +/examples/ @awslabs/smithy-rs-server # Python Server -/codegen-server-test/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server -/codegen-server/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-http-server-python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server +/codegen-server-test/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server +/codegen-server/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-http-server-python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server + +# Typescript Server +/codegen-server-test/typescript/ @awslabs/smithy-rs-typescript-server @awslabs/smithy-rs-server +/codegen-server/typescript/ @awslabs/smithy-rs-typescript-server @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-http-server-typescript/ @awslabs/smithy-rs-typescript-server @awslabs/smithy-rs-server # Shared ownership -/.github/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/CHANGELOG.md @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/CHANGELOG.next.toml @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/README.md @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/build.gradle.kts @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/buildSrc/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/codegen-core/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/design/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/gradle.properties @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/tools/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-async/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-eventstream/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-http/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-json/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-protocol-test/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-types/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-types-convert/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-xml/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/inlineable/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/build.gradle.kts @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/Cargo.toml @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/.github/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/CHANGELOG.md @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/CHANGELOG.next.toml @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/README.md @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/build.gradle.kts @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/buildSrc/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/codegen-core/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/design/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/gradle.properties @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/tools/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-async/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-eventstream/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-http/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-json/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-protocol-test/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-types/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-types-convert/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-xml/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/inlineable/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/build.gradle.kts @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/Cargo.toml @awslabs/rust-sdk-owners @awslabs/smithy-rs-server diff --git a/aws/SDK_CHANGELOG.next.json b/aws/SDK_CHANGELOG.next.json index 6d3e509f8e..cb5a407d32 100644 --- a/aws/SDK_CHANGELOG.next.json +++ b/aws/SDK_CHANGELOG.next.json @@ -6,65 +6,156 @@ "smithy-rs": [], "aws-sdk-rust": [ { - "message": "Integrate Endpoints 2.0 into the Rust SDK. Endpoints 2.0 enables features like S3 virtual addressing & S3\nobject lambda. As part of this change, there are several breaking changes although efforts have been made to deprecate\nwhere possible to smooth the upgrade path.\n1. `aws_smithy_http::endpoint::Endpoint` and the `endpoint_resolver` methods have been deprecated. In general, these usages\n should be replaced with usages of `endpoint_url` instead. `endpoint_url` accepts a string so an `aws_smithy_http::Endpoint`\n does not need to be constructed. This structure and methods will be removed in a future release.\n2. The `endpoint_resolver` method on `::config::Builder` now accepts a service specific endpoint resolver instead\n of an implementation of `ResolveAwsEndpoint`. Most users will be able to replace these usages with a usage of `endpoint_url`.\n3. `ResolveAwsEndpoint` has been deprecated and will be removed in a future version of the SDK.\n4. The SDK does not support \"pseudo regions\" anymore. Specifically, regions like `iam-fips` will no longer resolve to a FIPS endpoint.\n", + "message": "Request IDs can now be easily retrieved on successful responses. For example, with S3:\n```rust\n// Import the trait to get the `request_id` method on outputs\nuse aws_sdk_s3::types::RequestId;\nlet output = client.list_buckets().send().await?;\nprintln!(\"Request ID: {:?}\", output.request_id());\n```\n", "meta": { "bug": false, "breaking": true, - "tada": true + "tada": false }, - "author": "rcoh", + "author": "jdisanti", "references": [ - "smithy-rs#1784", - "smithy-rs#2074" + "smithy-rs#76", + "smithy-rs#2129" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 }, { - "message": "Add additional configuration parameters to `aws_sdk_s3::Config`.\n\nThe launch of endpoints 2.0 includes more configuration options for S3. The default behavior for endpoint resolution has\nbeen changed. Before, all requests hit the path-style endpoint. Going forward, all requests that can be routed to the\nvirtually hosted bucket will be routed there automatically.\n- `force_path_style`: Requests will now default to the virtually-hosted endpoint `.s3..amazonaws.com`\n- `use_arn_region`: Enables this client to use an ARN’s region when constructing an endpoint instead of the client’s configured region.\n- `accelerate`: Enables this client to use S3 Transfer Acceleration endpoints.\n\nNote: the AWS SDK for Rust does not currently support Multi Region Access Points (MRAP).\n", + "message": "Retrieving a request ID from errors now requires importing the `RequestId` trait. For example, with S3:\n```rust\nuse aws_sdk_s3::types::RequestId;\nprintln!(\"Request ID: {:?}\", error.request_id());\n```\n", "meta": { "bug": false, "breaking": true, - "tada": true + "tada": false }, - "author": "rcoh", + "author": "jdisanti", "references": [ - "smithy-rs#1784", - "smithy-rs#2074" + "smithy-rs#76", + "smithy-rs#2129" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 }, { - "message": "Move types for AWS SDK credentials to a separate crate.\nA new AWS runtime crate called `aws-credential-types` has been introduced. Types for AWS SDK credentials have been moved to that crate from `aws-config` and `aws-types`. The new crate is placed at the top of the dependency graph among AWS runtime crates with the aim of the downstream crates having access to the types defined in it.\n", + "message": "The `message()` and `code()` methods on errors have been moved into `ProvideErrorMetadata` trait. This trait will need to be imported to continue calling these.", "meta": { "bug": false, "breaking": true, "tada": false }, + "author": "jdisanti", + "references": [ + "smithy-rs#76", + "smithy-rs#2129" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "The `*Error` and `*ErrorKind` types have been combined to make error matching simpler.\n
\nExample with S3\n**Before:**\n```rust\nlet result = client\n .get_object()\n .bucket(BUCKET_NAME)\n .key(\"some-key\")\n .send()\n .await;\nmatch result {\n Ok(_output) => { /* Do something with the output */ }\n Err(err) => match err.into_service_error() {\n GetObjectError { kind, .. } => match kind {\n GetObjectErrorKind::InvalidObjectState(value) => println!(\"invalid object state: {:?}\", value),\n GetObjectErrorKind::NoSuchKey(_) => println!(\"object didn't exist\"),\n }\n err @ GetObjectError { .. } if err.code() == Some(\"SomeUnmodeledError\") => {}\n err @ _ => return Err(err.into()),\n },\n}\n```\n**After:**\n```rust\n// Needed to access the `.code()` function on the error type:\nuse aws_sdk_s3::types::ProvideErrorMetadata;\nlet result = client\n .get_object()\n .bucket(BUCKET_NAME)\n .key(\"some-key\")\n .send()\n .await;\nmatch result {\n Ok(_output) => { /* Do something with the output */ }\n Err(err) => match err.into_service_error() {\n GetObjectError::InvalidObjectState(value) => {\n println!(\"invalid object state: {:?}\", value);\n }\n GetObjectError::NoSuchKey(_) => {\n println!(\"object didn't exist\");\n }\n err if err.code() == Some(\"SomeUnmodeledError\") => {}\n err @ _ => return Err(err.into()),\n },\n}\n```\n
\n", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "jdisanti", + "references": [ + "smithy-rs#76", + "smithy-rs#2129", + "smithy-rs#2075" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "jdisanti", + "references": [ + "smithy-rs#76", + "smithy-rs#2129" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "Fluent builder methods on the client are now marked as deprecated when the related operation is deprecated.", + "meta": { + "bug": true, + "breaking": false, + "tada": true + }, + "author": "Velfi", + "references": [ + "aws-sdk-rust#740" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "`SdkError` variants can now be constructed for easier unit testing.", + "meta": { + "bug": false, + "breaking": false, + "tada": true + }, + "author": "jdisanti", + "references": [ + "smithy-rs#2428", + "smithy-rs#2208" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "Add more client re-exports. Specifically, it re-exports `aws_smithy_http::body::SdkBody`, `aws_smithy_http::byte_stream::error::Error`, and `aws_smithy_http::operation::{Request, Response}`.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, "author": "ysaito1001", "references": [ - "smithy-rs#2108" + "smithy-rs#2437", + "aws-sdk-rust#600" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 }, { - "message": "Add support for overriding profile name and profile file location across all providers. Prior to this change, each provider needed to be updated individually.\n\n### Before\n```rust\nuse aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider};\nuse aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind};\n\nlet profile_files = ProfileFiles::builder()\n .with_file(ProfileFileKind::Credentials, \"some/path/to/credentials-file\")\n .build();\nlet credentials_provider = ProfileFileCredentialsProvider::builder()\n .profile_files(profile_files.clone())\n .build();\nlet region_provider = ProfileFileRegionProvider::builder()\n .profile_files(profile_files)\n .build();\n\nlet sdk_config = aws_config::from_env()\n .credentials_provider(credentials_provider)\n .region(region_provider)\n .load()\n .await;\n```\n\n### After\n```rust\nuse aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider};\nuse aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind};\n\nlet profile_files = ProfileFiles::builder()\n .with_file(ProfileFileKind::Credentials, \"some/path/to/credentials-file\")\n .build();\nlet sdk_config = aws_config::from_env()\n .profile_files(profile_files)\n .load()\n .await;\n/// ```\n", + "message": "Enable presigning for S3's `HeadObject` operation.", "meta": { "bug": false, "breaking": false, + "tada": true + }, + "author": "Velfi", + "references": [ + "aws-sdk-rust#753", + "smithy-rs#2451" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "The modules in the SDK crates have been reorganized. See the [SDK Crate Reorganization Upgrade Guidance](https://github.com/awslabs/aws-sdk-rust/discussions/752) to see how to fix your code after this change.", + "meta": { + "bug": false, + "breaking": true, "tada": false }, - "author": "rcoh", + "author": "jdisanti", "references": [ - "smithy-rs#2152" + "smithy-rs#2433" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 }, { - "message": "`aws_config::profile::retry_config` && `aws_config::environment::retry_config` have been removed. Use `aws_config::default_provider::retry_config` instead.", + "message": "Reconnect on transient errors.\n\nIf a transient error (timeout, 500, 503, 503) is encountered, the connection will be evicted from the pool and will not\nbe reused. This is enabled by default for all AWS services. It can be disabled by setting `RetryConfig::with_reconnect_mode`\n\nAlthough there is no API breakage from this change, it alters the client behavior in a way that may cause breakage for customers.\n", "meta": { "bug": false, "breaking": true, @@ -72,57 +163,171 @@ }, "author": "rcoh", "references": [ - "smithy-rs#2162" + "aws-sdk-rust#160", + "smithy-rs#2445" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 }, { - "message": "Add support for resolving FIPS and dual-stack endpoints.\n\nFIPS and dual-stack endpoints can each be configured in multiple ways:\n1. Automatically from the environment and AWS profile\n2. Across all clients loaded from the same `SdkConfig` via `from_env().use_dual_stack(true).load().await`\n3. At a client level when constructing the configuration for an individual client.\n\nNote: Not all services support FIPS and dual-stack.\n", + "message": "Update MSRV to 1.66.1", "meta": { "bug": false, - "breaking": false, + "breaking": true, "tada": true }, + "author": "Velfi", + "references": [ + "smithy-rs#2467" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "Default connector provided by `aws-config` now respects `ConnectorSettings`.\n\nPreviously, it used the timeout settings provided by aws-config. A test from @Oliboy50 has been incorporated to verify this behavior.\n\n**Behavior Change**: Prior to this change, the Hyper client would be shared between all service clients. After this change, each service client will use its own Hyper Client.\nTo revert to the previous behavior, set `HttpConnector::Prebuilt` on `SdkConfig::http_connector`.\n", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, "author": "rcoh", "references": [ - "smithy-rs#2168" + "smithy-rs#2471", + "smithy-rs#2333", + "smithy-rs#2151" ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 }, { - "message": "Improve SDK credentials caching through type safety. `LazyCachingCredentialsProvider` has been renamed to `LazyCredentialsCache` and is no longer treated as a credentials provider. Furthermore, you do not create a `LazyCredentialsCache` directly, and instead you interact with `CredentialsCache`. This introduces the following breaking changes.\n\nIf you previously used `LazyCachingCredentialsProvider`, you can replace it with `CredentialsCache`.\n
\nExample\n\nBefore:\n```rust\nuse aws_config::meta::credentials::lazy_caching::LazyCachingCredentialsProvider;\nuse aws_types::provider::ProvideCredentials;\n\nfn make_provider() -> impl ProvideCredentials {\n // --snip--\n}\n\nlet credentials_provider =\n LazyCachingCredentialsProvider::builder()\n .load(make_provider())\n .build();\n\nlet sdk_config = aws_config::from_env()\n .credentials_provider(credentials_provider)\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nAfter:\n```rust\nuse aws_credential_types::cache::CredentialsCache;\nuse aws_types::provider::ProvideCredentials;\n\nfn make_provider() -> impl ProvideCredentials {\n // --snip--\n}\n\n// Wrapping a result of `make_provider` in `LazyCredentialsCache` is done automatically.\nlet sdk_config = aws_config::from_env()\n .credentials_cache(CredentialsCache::lazy()) // This line can be omitted because it is on by default.\n .credentials_provider(make_provider())\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nIf you previously configured a `LazyCachingCredentialsProvider`, you can use the builder for `LazyCredentialsCache` instead.\n\nBefore:\n```rust\nuse aws_config::meta::credentials::lazy_caching::LazyCachingCredentialsProvider;\nuse aws_types::provider::ProvideCredentials;\nuse std::time::Duration;\n\nfn make_provider() -> impl ProvideCredentials {\n // --snip--\n}\n\nlet credentials_provider =\n LazyCachingCredentialsProvider::builder()\n .load(make_provider())\n .load_timeout(Duration::from_secs(60)) // Configures timeout.\n .build();\n\nlet sdk_config = aws_config::from_env()\n .credentials_provider(credentials_provider)\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nAfter:\n```rust\nuse aws_credential_types::cache::CredentialsCache;\nuse aws_types::provider::ProvideCredentials;\nuse std::time::Duration;\n\nfn make_provider() -> impl ProvideCredentials {\n // --snip--\n}\n\nlet sdk_config = aws_config::from_env()\n .credentials_cache(\n CredentialsCache::lazy_builder()\n .load_timeout(Duration::from_secs(60)) // Configures timeout.\n .into_credentials_cache(),\n )\n .credentials_provider(make_provider())\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nThe examples above only demonstrate how to use `credentials_cache` and `credentials_provider` methods on `aws_config::ConfigLoader` but the same code update can be applied when you interact with `aws_types::sdk_config::Builder` or the builder for a service-specific config, e.g. `aws_sdk_s3::config::Builder`.\n\n
\n\n\nIf you previously configured a `DefaultCredentialsChain` by calling `load_timeout`, `buffer_time`, or `default_credential_expiration` on its builder, you need to call the same set of methods on the builder for `LazyCredentialsCache` instead.\n
\nExample\n\nBefore:\n```rust\nuse aws_config::default_provider::credentials::DefaultCredentialsChain;\nuse std::time::Duration;\n\nlet credentials_provider = DefaultCredentialsChain::builder()\n .buffer_time(Duration::from_secs(30))\n .default_credential_expiration(Duration::from_secs(20 * 60))\n .build()\n .await;\n\nlet sdk_config = aws_config::from_env()\n .credentials_provider(credentials_provider)\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nAfter:\n```rust\nuse aws_config::default_provider::credentials::default_provider;\nuse aws_credential_types::cache::CredentialsCache;\nuse std::time::Duration;\n\n// Previously used methods no longer exist on the builder for `DefaultCredentialsChain`.\nlet credentials_provider = default_provider().await;\n\nlet sdk_config = aws_config::from_env()\n .credentials_cache(\n CredentialsCache::lazy_builder()\n .buffer_time(Duration::from_secs(30))\n .default_credential_expiration(Duration::from_secs(20 * 60))\n .into_credentials_cache(),\n )\n .credentials_provider(credentials_provider)\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\n
\n", + "message": "Remove deprecated `ResolveAwsEndpoint` interfaces.\n[For details see the longform changelog entry](https://github.com/awslabs/aws-sdk-rust/discussions/755).\n", "meta": { "bug": false, "breaking": true, "tada": false }, - "author": "ysaito1001", + "author": "rcoh", + "references": [ + "smithy-rs#2390", + "smithy-rs#1784" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "Increase Tokio version to 1.23.1 for all crates. This is to address [RUSTSEC-2023-0001](https://rustsec.org/advisories/RUSTSEC-2023-0001)", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#2474" + ], + "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 4 + }, + { + "message": "Implement std::error::Error#source() properly for the service meta Error enum.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "abusch", "references": [ - "smithy-rs#2122", - "smithy-rs#2227" + "aws-sdk-rust#784" ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", "age": 3 }, { - "message": "The introduction of `CredentialsCache` comes with an accompanying type `SharedCredentialsCache`, which we will store in the property bag instead of a `SharedCredentialsProvider`. As a result, `aws_http::auth:set_provider` has been updated to `aws_http::auth::set_credentials_cache`.\n\nBefore:\n```rust\nuse aws_credential_types::Credentials;\nuse aws_credential_types::provider::SharedCredentialsProvider;\nuse aws_http::auth::set_provider;\nuse aws_smithy_http::body::SdkBody;\nuse aws_smithy_http::operation;\n\nlet mut req = operation::Request::new(http::Request::new(SdkBody::from(\"some body\")));\nlet credentials = Credentials::new(\"example\", \"example\", None, None, \"my_provider_name\");\nset_provider(\n &mut req.properties_mut(),\n SharedCredentialsProvider::new(credentials),\n);\n```\n\nAfter:\n```rust\nuse aws_credential_types::Credentials;\nuse aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache};\nuse aws_credential_types::provider::SharedCredentialsProvider;\nuse aws_http::auth::set_credentials_cache;\nuse aws_smithy_http::body::SdkBody;\nuse aws_smithy_http::operation;\n\nlet mut req = operation::Request::new(http::Request::new(SdkBody::from(\"some body\")));\nlet credentials = Credentials::new(\"example\", \"example\", None, None, \"my_provider_name\");\nlet credentials_cache = CredentialsCache::lazy_builder()\n .into_credentials_cache()\n .create_cache(SharedCredentialsProvider::new(credentials));\nset_credentials_cache(\n &mut req.properties_mut(),\n SharedCredentialsCache::new(credentials_cache),\n);\n```\n", + "message": "The outputs for event stream operations (for example, S3's SelectObjectContent) now implement the `Sync` auto-trait.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "jdisanti", + "references": [ + "smithy-rs#2496" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 3 + }, + { + "message": "The AWS SDK now compiles for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!", "meta": { "bug": false, - "breaking": true, + "breaking": false, + "tada": true + }, + "author": "eduardomourar", + "references": [ + "smithy-rs#2254" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 3 + }, + { + "message": "S3's `GetObject` will no longer panic when checksum validation is enabled and the target object was uploaded as a multi-part upload.\nHowever, these objects cannot be checksum validated by the SDK due to the way checksums are calculated for multipart uploads.\nFor more information, see [this page](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums).\n", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "Velfi", + "references": [ + "aws-sdk-rust#764" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 3 + }, + { + "message": "`AppName` is now configurable from within `ConfigLoader`.", + "meta": { + "bug": true, + "breaking": false, "tada": false }, "author": "ysaito1001", "references": [ - "smithy-rs#2122", - "smithy-rs#2227" + "smithy-rs#2513" ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", "age": 3 }, { - "message": "Fix endpoint for s3.write_get_object_response(). This bug was introduced in 0.53.", + "message": "Add support for omitting session token in canonical requests for SigV4 signing.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "martinjlowm", + "references": [ + "smithy-rs#2473" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 3 + }, + { + "message": "Add `into_segments` method to `AggregatedBytes`, for zero-copy conversions.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "parker-timmerman", + "references": [ + "smithy-rs#2525" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 3 + }, + { + "message": "Fix bug where an incorrect endpoint was produced for `WriteGetObjectResponse`", "meta": { "bug": true, "breaking": false, @@ -130,13 +335,42 @@ }, "author": "rcoh", "references": [ - "smithy-rs#2204" + "smithy-rs#781", + "aws-sdk-rust#781" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 3 + }, + { + "message": "Update the `std::fmt::Debug` implementation for `aws-sigv4::SigningParams` so that it will no longer print sensitive information.", + "meta": { + "bug": true, + "breaking": false, + "tada": true + }, + "author": "Velfi", + "references": [ + "smithy-rs#2562" ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", "age": 3 }, { - "message": "Add `with_test_defaults()` and `set_test_defaults()` to `::Config`. These methods fill in defaults for configuration that is mandatory to successfully send a request.", + "message": "`aws_smithy_types::date_time::Format` has been re-exported in SDK crates.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2534" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 3 + }, + { + "message": "Reduce several instances of credential exposure in the SDK logs:\n- IMDS now suppresses the body of the response from logs\n- `aws-sigv4` marks the `x-amz-session-token` header as sensitive\n- STS & SSO credentials have been manually marked as sensitive which suppresses logging of response bodies for relevant operations\n", "meta": { "bug": false, "breaking": false, @@ -144,13 +378,13 @@ }, "author": "rcoh", "references": [ - "smithy-rs#2204" + "smithy-rs#2603" ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", "age": 3 }, { - "message": "Request IDs can now be easily retrieved on successful responses. For example, with S3:\n```rust\n// Import the trait to get the `request_id` method on outputs\nuse aws_sdk_s3::types::RequestId;\nlet output = client.list_buckets().send().await?;\nprintln!(\"Request ID: {:?}\", output.request_id());\n```\n", + "message": "Update MSRV to Rust 1.67.1", "meta": { "bug": false, "breaking": true, @@ -158,60 +392,99 @@ }, "author": "jdisanti", "references": [ - "smithy-rs#76", - "smithy-rs#2129" + "smithy-rs#2611" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 3 + }, + { + "message": "Avoid extending IMDS credentials' expiry unconditionally, which may incorrectly extend it beyond what is originally defined; If returned credentials are not stale, use them as they are.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2687", + "smithy-rs#2694" + ], + "since-commit": "3b5fc51a41700c88270145e38fa708eca72dc414", + "age": 2 + }, + { + "message": "Automatically exclude X-Ray trace ID headers and authorization headers from SigV4 canonical request calculations.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "relevantsam", + "references": [ + "smithy-rs#2815" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Retrieving a request ID from errors now requires importing the `RequestId` trait. For example, with S3:\n```rust\nuse aws_sdk_s3::types::RequestId;\nprintln!(\"Request ID: {:?}\", error.request_id());\n```\n", + "message": "Add accessors to Builders", "meta": { "bug": false, - "breaking": true, + "breaking": false, "tada": false }, - "author": "jdisanti", + "author": "davidsouther", "references": [ - "smithy-rs#76", - "smithy-rs#2129" + "smithy-rs#2791" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "The `message()` and `code()` methods on errors have been moved into `ProvideErrorMetadata` trait. This trait will need to be imported to continue calling these.", + "message": "Remove native-tls and add a migration guide.", "meta": { "bug": false, "breaking": true, "tada": false }, - "author": "jdisanti", + "author": "82marbag", "references": [ - "smithy-rs#76", - "smithy-rs#2129" + "smithy-rs#2675" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "The `*Error` and `*ErrorKind` types have been combined to make error matching simpler.\n
\nExample with S3\n**Before:**\n```rust\nlet result = client\n .get_object()\n .bucket(BUCKET_NAME)\n .key(\"some-key\")\n .send()\n .await;\nmatch result {\n Ok(_output) => { /* Do something with the output */ }\n Err(err) => match err.into_service_error() {\n GetObjectError { kind, .. } => match kind {\n GetObjectErrorKind::InvalidObjectState(value) => println!(\"invalid object state: {:?}\", value),\n GetObjectErrorKind::NoSuchKey(_) => println!(\"object didn't exist\"),\n }\n err @ GetObjectError { .. } if err.code() == Some(\"SomeUnmodeledError\") => {}\n err @ _ => return Err(err.into()),\n },\n}\n```\n**After:**\n```rust\n// Needed to access the `.code()` function on the error type:\nuse aws_sdk_s3::types::ProvideErrorMetadata;\nlet result = client\n .get_object()\n .bucket(BUCKET_NAME)\n .key(\"some-key\")\n .send()\n .await;\nmatch result {\n Ok(_output) => { /* Do something with the output */ }\n Err(err) => match err.into_service_error() {\n GetObjectError::InvalidObjectState(value) => {\n println!(\"invalid object state: {:?}\", value);\n }\n GetObjectError::NoSuchKey(_) => {\n println!(\"object didn't exist\");\n }\n err if err.code() == Some(\"SomeUnmodeledError\") => {}\n err @ _ => return Err(err.into()),\n },\n}\n```\n
\n", + "message": "Fix error message when `credentials-sso` feature is not enabled on `aws-config`. NOTE: if you use `no-default-features`, you will need to manually able `credentials-sso` after 0.55.*", "meta": { - "bug": false, - "breaking": true, + "bug": true, + "breaking": false, "tada": false }, - "author": "jdisanti", + "author": "rcoh", "references": [ - "smithy-rs#76", - "smithy-rs#2129", - "smithy-rs#2075" + "smithy-rs#2722", + "aws-sdk-rust#703" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`.", + "message": "`SsoCredentialsProvider`, `AssumeRoleProvider`, and `WebIdentityTokenCredentialsProvider` now use `NoCredentialsCache` internally when fetching credentials using an STS client. This avoids double-caching when these providers are wrapped by `LazyCredentialsCache` when a service client is created.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2720" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "For event stream operations such as S3 SelectObjectContent or Transcribe StartStreamTranscription, the `EventStreamSender` in the input now requires the passed in `Stream` impl to implement `Sync`.", "meta": { "bug": false, "breaking": true, @@ -219,132 +492,160 @@ }, "author": "jdisanti", "references": [ - "smithy-rs#76", - "smithy-rs#2129" + "smithy-rs#2673" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Fluent builder methods on the client are now marked as deprecated when the related operation is deprecated.", + "message": "The `SigningInstructions` in the `aws-sigv4` module are now public. This allows them to be named in a function signature.", "meta": { "bug": true, "breaking": false, - "tada": true + "tada": false }, - "author": "Velfi", + "author": "cholcombe973", "references": [ - "aws-sdk-rust#740" + "smithy-rs#2730" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "`SdkError` variants can now be constructed for easier unit testing.", + "message": "Time is now controlled by the `TimeSource` trait. This facilitates testing as well as use cases like WASM where `SystemTime::now()` is not supported.", "meta": { "bug": false, "breaking": false, - "tada": true + "tada": false }, - "author": "jdisanti", + "author": "rcoh", "references": [ - "smithy-rs#2428", - "smithy-rs#2208" + "smithy-rs#2728", + "smithy-rs#2262", + "aws-sdk-rust#2087" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Add more client re-exports. Specifically, it re-exports `aws_smithy_http::body::SdkBody`, `aws_smithy_http::byte_stream::error::Error`, and `aws_smithy_http::operation::{Request, Response}`.", + "message": "The SDK has added support for timestreamwrite and timestreamquery. Support for these services is considered experimental at this time. In order to use these services, you MUST call `.with_endpoint_discovery_enabled()` on the `Client` after construction.", "meta": { "bug": false, "breaking": false, + "tada": true + }, + "author": "rcoh", + "references": [ + "smithy-rs#2707", + "aws-sdk-rust#114", + "smithy-rs#2846" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "A newtype wrapper `SharedAsyncSleep` has been introduced and occurrences of `Arc` that appear in public APIs have been replaced with it.", + "meta": { + "bug": false, + "breaking": true, "tada": false }, "author": "ysaito1001", "references": [ - "smithy-rs#2437", - "aws-sdk-rust#600" + "smithy-rs#2742" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Enable presigning for S3's `HeadObject` operation.", + "message": "Update MSRV to Rust 1.69.0", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "Velfi", + "references": [ + "smithy-rs#2893" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "Implement unstable serde support for the `Number`, `Blob`, `Document`, `DateTime` primitives", "meta": { "bug": false, "breaking": false, "tada": true }, - "author": "Velfi", + "author": "thomas-k-cameron", "references": [ - "aws-sdk-rust#753", - "smithy-rs#2451" + "smithy-rs#2647", + "smithy-rs#2645", + "smithy-rs#2646", + "smithy-rs#2616" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "The modules in the SDK crates have been reorganized. See the [SDK Crate Reorganization Upgrade Guidance](https://github.com/awslabs/aws-sdk-rust/discussions/752) to see how to fix your code after this change.", + "message": "Add a `send_with` function on `-Input` types for sending requests without fluent builders", "meta": { "bug": false, - "breaking": true, - "tada": false + "breaking": false, + "tada": true }, - "author": "jdisanti", + "author": "thomas-k-cameron", "references": [ - "smithy-rs#2433" + "smithy-rs#2652" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Reconnect on transient errors.\n\nIf a transient error (timeout, 500, 503, 503) is encountered, the connection will be evicted from the pool and will not\nbe reused. This is enabled by default for all AWS services. It can be disabled by setting `RetryConfig::with_reconnect_mode`\n\nAlthough there is no API breakage from this change, it alters the client behavior in a way that may cause breakage for customers.\n", + "message": "The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`.", "meta": { "bug": false, "breaking": true, "tada": false }, - "author": "rcoh", + "author": "ysaito1001", "references": [ - "aws-sdk-rust#160", - "smithy-rs#2445" + "smithy-rs#2783" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Update MSRV to 1.66.1", + "message": "The implementation `From` for `aws_http::user_agent::UserAgentStageError` has been removed.", "meta": { "bug": false, "breaking": true, - "tada": true + "tada": false }, - "author": "Velfi", + "author": "ysaito1001", "references": [ - "smithy-rs#2467" + "smithy-rs#2845" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Default connector provided by `aws-config` now respects `ConnectorSettings`.\n\nPreviously, it used the timeout settings provided by aws-config. A test from @Oliboy50 has been incorporated to verify this behavior.\n\n**Behavior Change**: Prior to this change, the Hyper client would be shared between all service clients. After this change, each service client will use its own Hyper Client.\nTo revert to the previous behavior, set `HttpConnector::Prebuilt` on `SdkConfig::http_connector`.\n", + "message": "The AppName property can now be set with `sdk_ua_app_id` in profile files. The old field, `sdk-ua-app-id`, is maintained for backwards compatibility.", "meta": { - "bug": true, + "bug": false, "breaking": false, "tada": false }, "author": "rcoh", "references": [ - "smithy-rs#2471", - "smithy-rs#2333", - "smithy-rs#2151" + "smithy-rs#2724" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Remove deprecated `ResolveAwsEndpoint` interfaces.\n[For details see the longform changelog entry](https://github.com/awslabs/aws-sdk-rust/discussions/755).\n", + "message": "**Behavior change**: Credential providers now share the HTTP connector used by the SDK. If you want to keep a separate connector for clients, use `::ConfigBuilder::http_connector` when constructing the client.", "meta": { "bug": false, "breaking": true, @@ -352,24 +653,50 @@ }, "author": "rcoh", "references": [ - "smithy-rs#2390", - "smithy-rs#1784" + "aws-sdk-rust#579", + "aws-sdk-rust#338" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 }, { - "message": "Increase Tokio version to 1.23.1 for all crates. This is to address [RUSTSEC-2023-0001](https://rustsec.org/advisories/RUSTSEC-2023-0001)", + "message": "The `doc(hidden)` `time_source` in `aws-credential-types` was removed. Use `aws_smithy_async::time` instead.", "meta": { "bug": false, - "breaking": false, + "breaking": true, "tada": false }, "author": "rcoh", "references": [ - "smithy-rs#2474" + "smithy-rs#2877" ], - "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The `doc(hidden)` `with_env` in `ProviderConfig` was removed.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#2877" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The underlying architecture of the SDK clients has been overhauled. This shouldn't require any changes for most projects, but will affect projects that customize the SDK middleware. More details are available in the [upgrade guide](https://github.com/awslabs/aws-sdk-rust/discussions/853) if you are effected by these changes.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "jdisanti", + "references": [], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 } ], diff --git a/aws/rust-runtime/Cargo.toml b/aws/rust-runtime/Cargo.toml index b4e4670550..0e32e03bfc 100644 --- a/aws/rust-runtime/Cargo.toml +++ b/aws/rust-runtime/Cargo.toml @@ -8,9 +8,11 @@ members = [ "aws-http", "aws-hyper", "aws-inlineable", + "aws-runtime", + "aws-runtime-api", "aws-sig-auth", + "aws-sigv4", "aws-types", - "aws-sigv4" ] exclude = ["aws-config"] diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 92cacc1c52..60fb0f5261 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -10,8 +10,9 @@ repository = "https://github.com/awslabs/smithy-rs" [features] client-hyper = ["aws-smithy-client/client-hyper"] -rustls = ["aws-smithy-client/rustls"] -native-tls = ["aws-smithy-client/native-tls"] +rustls = ["aws-smithy-client/rustls", "client-hyper"] +native-tls = [] +allow-compilation = [] # our tests use `cargo test --all-features` and native-tls breaks CI rt-tokio = ["aws-smithy-async/rt-tokio", "tokio/rt"] credentials-sso = ["dep:aws-sdk-sso", "dep:ring", "dep:hex", "dep:zeroize"] @@ -28,13 +29,13 @@ aws-smithy-http-tower = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-http-to aws-smithy-json = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-json" } aws-smithy-types = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-types" } aws-types = { path = "../../sdk/build/aws-sdk/sdk/aws-types" } -hyper = { version = "0.14.25", default-features = false } +hyper = { version = "0.14.26", default-features = false } time = { version = "0.3.4", features = ["parsing"] } tokio = { version = "1.13.1", features = ["sync"] } tracing = { version = "0.1" } # implementation detail of IMDS credentials provider -fastrand = "1" +fastrand = "2.0.0" bytes = "1.1.0" http = "0.2.4" @@ -49,21 +50,23 @@ zeroize = { version = "1", optional = true } [dev-dependencies] futures-util = { version = "0.3.16", default-features = false } tracing-test = "0.2.1" +tracing-subscriber = { version = "0.3.16", features = ["fmt", "json"] } tokio = { version = "1.23.1", features = ["full", "test-util"] } # used for fuzzing profile parsing -arbitrary = "=1.1.3" # 1.1.4 requires Rust 1.63 to compile +arbitrary = "1.3" # used for test case deserialization serde = { version = "1", features = ["derive"] } serde_json = "1" aws-credential-types = { path = "../../sdk/build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } -aws-smithy-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"] } +aws-smithy-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rt-tokio", "client-hyper"] } # used for a usage example -hyper-rustls = { version = "0.23.0", features = ["webpki-tokio", "http2", "http1"] } +hyper-rustls = { version = "0.24", features = ["webpki-tokio", "http2", "http1"] } +aws-smithy-async = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] } [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-config/clippy.toml b/aws/rust-runtime/aws-config/clippy.toml new file mode 120000 index 0000000000..85f6167cb1 --- /dev/null +++ b/aws/rust-runtime/aws-config/clippy.toml @@ -0,0 +1 @@ +../clippy.toml \ No newline at end of file diff --git a/aws/rust-runtime/aws-config/external-types.toml b/aws/rust-runtime/aws-config/external-types.toml index 7e0257f46d..b90a84885c 100644 --- a/aws/rust-runtime/aws-config/external-types.toml +++ b/aws/rust-runtime/aws-config/external-types.toml @@ -9,7 +9,9 @@ allowed_external_types = [ "aws_credential_types::provider::SharedCredentialsProvider", "aws_sdk_sts::types::_policy_descriptor_type::PolicyDescriptorType", "aws_smithy_async::rt::sleep::AsyncSleep", + "aws_smithy_async::time::TimeSource", "aws_smithy_client::bounds::SmithyConnector", + "aws_smithy_client::conns::default_connector::default_connector", "aws_smithy_client::erase::DynConnector", "aws_smithy_client::erase::boxclone::BoxCloneService", "aws_smithy_client::http_connector::ConnectorSettings", diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs index f03d44ab6e..33c820261f 100644 --- a/aws/rust-runtime/aws-config/src/connector.rs +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -5,56 +5,28 @@ //! Functionality related to creating new HTTP Connectors -use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_client::erase::DynConnector; -use aws_smithy_client::http_connector::ConnectorSettings; -use std::sync::Arc; -// unused when all crate features are disabled /// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` -pub(crate) fn expect_connector(connector: Option) -> DynConnector { - connector.expect("No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this.") -} - -#[cfg(any(feature = "rustls", feature = "native-tls"))] -fn base( - settings: &ConnectorSettings, - sleep: Option>, -) -> aws_smithy_client::hyper_ext::Builder { - let mut hyper = - aws_smithy_client::hyper_ext::Adapter::builder().connector_settings(settings.clone()); - if let Some(sleep) = sleep { - hyper = hyper.sleep_impl(sleep); +pub(crate) fn expect_connector(for_what: &str, connector: Option) -> DynConnector { + if let Some(conn) = connector { + conn + } else { + panic!("{for_what} require(s) a HTTP connector, but none was available. Enable the `rustls` crate feature or set a connector to fix this.") } - hyper } -/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(feature = "rustls")] -pub fn default_connector( - settings: &ConnectorSettings, - sleep: Option>, -) -> Option { - tracing::trace!(settings = ?settings, sleep = ?sleep, "creating a new connector"); - let hyper = base(settings, sleep).build(aws_smithy_client::conns::https()); - Some(DynConnector::new(hyper)) -} +#[cfg(feature = "client-hyper")] +pub use aws_smithy_client::conns::default_connector; -/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(all(not(feature = "rustls"), feature = "native-tls"))] -pub fn default_connector( - settings: &ConnectorSettings, - sleep: Option>, -) -> Option { - let hyper = base(settings, sleep).build(aws_smithy_client::conns::native_tls()); - Some(DynConnector::new(hyper)) -} +#[cfg(all(feature = "native-tls", not(feature = "allow-compilation")))] +compile_error!("Feature native-tls has been removed. For upgrade instructions, see: https://awslabs.github.io/smithy-rs/design/transport/connector.html"); -/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(not(any(feature = "rustls", feature = "native-tls")))] +/// Given `ConnectorSettings` and a [`SharedAsyncSleep`](aws_smithy_async::rt::sleep::SharedAsyncSleep), create a `DynConnector` from defaults depending on what cargo features are activated. +#[cfg(not(feature = "client-hyper"))] pub fn default_connector( - _settings: &ConnectorSettings, - _sleep: Option>, + _settings: &aws_smithy_client::http_connector::ConnectorSettings, + _sleep: Option, ) -> Option { None } diff --git a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs index f6fce3d730..041d0f8261 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs @@ -3,16 +3,33 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::environment::app_name::EnvironmentVariableAppNameProvider; -use crate::profile::app_name; use crate::provider_config::ProviderConfig; -use aws_types::app_name::AppName; +use crate::standard_property::{PropertyResolutionError, StandardProperty}; +use aws_smithy_types::error::display::DisplayErrorContext; +use aws_types::app_name::{AppName, InvalidAppName}; /// Default App Name Provider chain /// /// This provider will check the following sources in order: -/// 1. [Environment variables](EnvironmentVariableAppNameProvider) -/// 2. [Profile file](crate::profile::app_name::ProfileFileAppNameProvider) +/// 1. Environment variables: `AWS_SDK_UA_APP_ID` +/// 2. Profile files from the key `sdk_ua_app_id` +/// +#[doc = include_str!("../profile/location_of_profile_files.md")] +/// +/// # Examples +/// +/// **Loads "my-app" as the app name** +/// ```ini +/// [default] +/// sdk_ua_app_id = my-app +/// ``` +/// +/// **Loads "my-app" as the app name _if and only if_ the `AWS_PROFILE` environment variable +/// is set to `other`.** +/// ```ini +/// [profile other] +/// sdk_ua_app_id = my-app +/// ``` pub fn default_provider() -> Builder { Builder::default() } @@ -20,8 +37,7 @@ pub fn default_provider() -> Builder { /// Default provider builder for [`AppName`] #[derive(Debug, Default)] pub struct Builder { - env_provider: EnvironmentVariableAppNameProvider, - profile_file: app_name::Builder, + provider_config: ProviderConfig, } impl Builder { @@ -29,23 +45,43 @@ impl Builder { /// Configure the default chain /// /// Exposed for overriding the environment when unit-testing providers - pub fn configure(mut self, configuration: &ProviderConfig) -> Self { - self.env_provider = EnvironmentVariableAppNameProvider::new_with_env(configuration.env()); - self.profile_file = self.profile_file.configure(configuration); - self + pub fn configure(self, configuration: &ProviderConfig) -> Self { + Self { + provider_config: configuration.clone(), + } } /// Override the profile name used by this provider pub fn profile_name(mut self, name: &str) -> Self { - self.profile_file = self.profile_file.profile_name(name); + self.provider_config = self.provider_config.with_profile_name(name.to_string()); self } + async fn fallback_app_name( + &self, + ) -> Result, PropertyResolutionError> { + StandardProperty::new() + .profile("sdk-ua-app-id") + .validate(&self.provider_config, |name| AppName::new(name.to_string())) + .await + } + /// Build an [`AppName`] from the default chain pub async fn app_name(self) -> Option { - self.env_provider - .app_name() - .or(self.profile_file.build().app_name().await) + let standard = StandardProperty::new() + .env("AWS_SDK_UA_APP_ID") + .profile("sdk_ua_app_id") + .validate(&self.provider_config, |name| AppName::new(name.to_string())) + .await; + let with_fallback = match standard { + Ok(None) => self.fallback_app_name().await, + other => other, + }; + + with_fallback.map_err( + |err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for App Name setting"), + ) + .unwrap_or(None) } } @@ -80,14 +116,11 @@ mod tests { // test that overriding profile_name on the root level is deprecated #[tokio::test] async fn profile_name_override() { - let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk-ua-app-id = correct")]); + let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk_ua_app_id = correct")]); let conf = crate::from_env() - .configure( - ProviderConfig::empty() - .with_fs(fs) - .with_sleep(InstantSleep) - .with_http_connector(no_traffic_connector()), - ) + .sleep_impl(InstantSleep) + .fs(fs) + .http_connector(no_traffic_connector()) .profile_name("custom") .profile_files( ProfileFiles::builder() @@ -101,6 +134,23 @@ mod tests { #[tokio::test] async fn load_from_profile() { + let fs = Fs::from_slice(&[("test_config", "[default]\nsdk_ua_app_id = correct")]); + let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]); + let app_name = Builder::default() + .configure( + &ProviderConfig::empty() + .with_fs(fs) + .with_env(env) + .with_http_connector(no_traffic_connector()), + ) + .app_name() + .await; + + assert_eq!(Some(AppName::new("correct").unwrap()), app_name); + } + + #[tokio::test] + async fn load_from_profile_old_name() { let fs = Fs::from_slice(&[("test_config", "[default]\nsdk-ua-app-id = correct")]); let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]); let app_name = Builder::default() diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index 7f04b334b5..a5e1fb0dc5 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -14,7 +14,7 @@ use crate::meta::credentials::CredentialsProviderChain; use crate::meta::region::ProvideRegion; use crate::provider_config::ProviderConfig; -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] /// Default Credentials Provider chain /// /// The region from the default region provider will be used @@ -170,8 +170,8 @@ impl Builder { /// Creates a `DefaultCredentialsChain` /// /// ## Panics - /// This function will panic if no connector has been set and neither `rustls` and `native-tls` - /// features have both been disabled. + /// This function will panic if no connector has been set or the `rustls` + /// feature has been disabled. pub async fn build(self) -> DefaultCredentialsChain { let region = match self.region_override { Some(provider) => provider.region().await, @@ -198,9 +198,9 @@ impl Builder { #[cfg(test)] mod test { - use tracing_test::traced_test; - use aws_credential_types::provider::ProvideCredentials; + use aws_smithy_async::time::StaticTimeSource; + use std::time::UNIX_EPOCH; use crate::default_provider::credentials::DefaultCredentialsChain; @@ -226,8 +226,8 @@ mod test { /// make_test!(live: test_name) /// ``` macro_rules! make_test { - ($name: ident) => { - make_test!($name, execute); + ($name: ident $(#[$m:meta])*) => { + make_test!($name, execute, $(#[$m])*); }; (update: $name:ident) => { make_test!($name, execute_and_update); @@ -235,14 +235,14 @@ mod test { (live: $name:ident) => { make_test!($name, execute_from_live_traffic); }; - ($name: ident, $func: ident) => { - make_test!($name, $func, std::convert::identity); + ($name: ident, $func: ident, $(#[$m:meta])*) => { + make_test!($name, $func, std::convert::identity $(, #[$m])*); }; - ($name: ident, $provider_config_builder: expr) => { + ($name: ident, builder: $provider_config_builder: expr) => { make_test!($name, execute, $provider_config_builder); }; - ($name: ident, $func: ident, $provider_config_builder: expr) => { - #[traced_test] + ($name: ident, $func: ident, $provider_config_builder: expr $(, #[$m:meta])*) => { + $(#[$m])* #[tokio::test] async fn $name() { crate::test_case::TestEnvironment::from_dir(concat!( @@ -252,11 +252,14 @@ mod test { .await .unwrap() .with_provider_config($provider_config_builder) - .$func(|conf| async { - crate::default_provider::credentials::Builder::default() - .configure(conf) - .build() - .await + .$func(|conf| { + let conf = conf.clone(); + async move { + crate::default_provider::credentials::Builder::default() + .configure(conf) + .build() + .await + } }) .await } @@ -277,29 +280,29 @@ mod test { make_test!(imds_no_iam_role); make_test!(imds_default_chain_error); - make_test!(imds_default_chain_success, |config| { - config.with_time_source(aws_credential_types::time_source::TimeSource::testing( - &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), - )) + make_test!(imds_default_chain_success, builder: |config| { + config.with_time_source(StaticTimeSource::new(UNIX_EPOCH)) }); make_test!(imds_assume_role); - make_test!(imds_config_with_no_creds, |config| { - config.with_time_source(aws_credential_types::time_source::TimeSource::testing( - &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), - )) + make_test!(imds_config_with_no_creds, builder: |config| { + config.with_time_source(StaticTimeSource::new(UNIX_EPOCH)) }); make_test!(imds_disabled); - make_test!(imds_default_chain_retries, |config| { - config.with_time_source(aws_credential_types::time_source::TimeSource::testing( - &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), - )) + make_test!(imds_default_chain_retries, builder: |config| { + config.with_time_source(StaticTimeSource::new(UNIX_EPOCH)) }); make_test!(ecs_assume_role); make_test!(ecs_credentials); make_test!(ecs_credentials_invalid_profile); + #[cfg(not(feature = "credentials-sso"))] + make_test!(sso_assume_role #[should_panic(expected = "This behavior requires following cargo feature(s) enabled: credentials-sso")]); + #[cfg(not(feature = "credentials-sso"))] + make_test!(sso_no_token_file #[should_panic(expected = "This behavior requires following cargo feature(s) enabled: credentials-sso")]); + #[cfg(feature = "credentials-sso")] make_test!(sso_assume_role); + #[cfg(feature = "credentials-sso")] make_test!(sso_no_token_file); @@ -324,12 +327,10 @@ mod test { } #[tokio::test] - #[traced_test] #[cfg(feature = "client-hyper")] async fn no_providers_configured_err() { use crate::provider_config::ProviderConfig; use aws_credential_types::provider::error::CredentialsError; - use aws_credential_types::time_source::TimeSource; use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_client::erase::boxclone::BoxCloneService; use aws_smithy_client::never::NeverConnected; @@ -337,7 +338,7 @@ mod test { tokio::time::pause(); let conf = ProviderConfig::no_configuration() .with_tcp_connector(BoxCloneService::new(NeverConnected::new())) - .with_time_source(TimeSource::default()) + .with_time_source(StaticTimeSource::new(UNIX_EPOCH)) .with_sleep(TokioSleep::new()); let provider = DefaultCredentialsChain::builder() .configure(conf) diff --git a/aws/rust-runtime/aws-config/src/environment/app_name.rs b/aws/rust-runtime/aws-config/src/environment/app_name.rs index 79c3abb6ac..3abf0fdedb 100644 --- a/aws/rust-runtime/aws-config/src/environment/app_name.rs +++ b/aws/rust-runtime/aws-config/src/environment/app_name.rs @@ -9,10 +9,12 @@ use aws_types::os_shim_internal::Env; /// Load an app name from the `AWS_SDK_UA_APP_ID` environment variable. #[derive(Debug, Default)] +#[deprecated(note = "This is unused and will be removed in a future release.")] pub struct EnvironmentVariableAppNameProvider { env: Env, } +#[allow(deprecated)] impl EnvironmentVariableAppNameProvider { /// Create a new `EnvironmentVariableAppNameProvider` pub fn new() -> Self { @@ -42,30 +44,3 @@ impl EnvironmentVariableAppNameProvider { } } } - -#[cfg(test)] -mod tests { - use crate::environment::EnvironmentVariableAppNameProvider; - use aws_types::app_name::AppName; - use aws_types::os_shim_internal::Env; - use std::collections::HashMap; - - #[test] - fn env_var_not_set() { - let provider = EnvironmentVariableAppNameProvider::new_with_env(Env::from(HashMap::new())); - assert_eq!(None, provider.app_name()); - } - - #[test] - fn env_var_set() { - let provider = EnvironmentVariableAppNameProvider::new_with_env(Env::from( - vec![("AWS_SDK_UA_APP_ID".to_string(), "something".to_string())] - .into_iter() - .collect::>(), - )); - assert_eq!( - Some(AppName::new("something").unwrap()), - provider.app_name() - ); - } -} diff --git a/aws/rust-runtime/aws-config/src/environment/mod.rs b/aws/rust-runtime/aws-config/src/environment/mod.rs index 1191947287..dd2c7cba3b 100644 --- a/aws/rust-runtime/aws-config/src/environment/mod.rs +++ b/aws/rust-runtime/aws-config/src/environment/mod.rs @@ -8,7 +8,6 @@ /// Load app name from the environment pub mod app_name; -pub use app_name::EnvironmentVariableAppNameProvider; use std::error::Error; use std::fmt::{Display, Formatter}; diff --git a/aws/rust-runtime/aws-config/src/http_credential_provider.rs b/aws/rust-runtime/aws-config/src/http_credential_provider.rs index df894f59ef..2568cc435d 100644 --- a/aws/rust-runtime/aws-config/src/http_credential_provider.rs +++ b/aws/rust-runtime/aws-config/src/http_credential_provider.rs @@ -100,7 +100,10 @@ impl Builder { .read_timeout(DEFAULT_READ_TIMEOUT) .build() }); - let connector = expect_connector(provider_config.connector(&connector_settings)); + let connector = expect_connector( + "The HTTP credentials provider", + provider_config.connector(&connector_settings), + ); let mut client_builder = aws_smithy_client::Client::builder() .connector(connector) .middleware(Identity::new()); @@ -149,6 +152,10 @@ impl ParseStrictResponse for CredentialsResponseParser { )), } } + + fn sensitive(&self) -> bool { + true + } } #[derive(Clone, Debug)] diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index 6bc8a290bf..f084bc9a83 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -280,6 +280,10 @@ impl ParseStrictResponse for ImdsGetResponseHandler { Err(InnerImdsError::BadStatus) } } + + fn sensitive(&self) -> bool { + true + } } /// IMDSv2 Endpoint Mode @@ -426,7 +430,10 @@ impl Builder { .read_timeout(self.read_timeout.unwrap_or(DEFAULT_READ_TIMEOUT)) .build(); let connector_settings = ConnectorSettings::from_timeout_config(&timeout_config); - let connector = expect_connector(config.connector(&connector_settings)); + let connector = expect_connector( + "The IMDS credentials provider", + config.connector(&connector_settings), + ); let endpoint_source = self .endpoint .unwrap_or_else(|| EndpointSource::Env(config.clone())); @@ -564,8 +571,8 @@ impl ClassifyRetry, SdkError> for ImdsResponseRetryClassi pub(crate) mod test { use crate::imds::client::{Client, EndpointMode, ImdsResponseRetryClassifier}; use crate::provider_config::ProviderConfig; - use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::test_util::instant_time_and_sleep; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::{capture_request, TestConnection}; use aws_smithy_client::{SdkError, SdkSuccess}; @@ -693,14 +700,13 @@ pub(crate) mod test { imds_response(r#"test-imds-output2"#), ), ]); - let mut time_source = TestingTimeSource::new(UNIX_EPOCH); - tokio::time::pause(); + let (time_source, sleep) = instant_time_and_sleep(UNIX_EPOCH); let client = super::Client::builder() .configure( &ProviderConfig::no_configuration() .with_http_connector(DynConnector::new(connection.clone())) - .with_time_source(TimeSource::testing(&time_source)) - .with_sleep(TokioSleep::new()), + .with_time_source(time_source.clone()) + .with_sleep(sleep), ) .endpoint_mode(EndpointMode::IpV6) .token_ttl(Duration::from_secs(600)) @@ -745,14 +751,13 @@ pub(crate) mod test { imds_response(r#"test-imds-output3"#), ), ]); - tokio::time::pause(); - let mut time_source = TestingTimeSource::new(UNIX_EPOCH); + let (time_source, sleep) = instant_time_and_sleep(UNIX_EPOCH); let client = super::Client::builder() .configure( &ProviderConfig::no_configuration() - .with_sleep(TokioSleep::new()) + .with_sleep(sleep) .with_http_connector(DynConnector::new(connection.clone())) - .with_time_source(TimeSource::testing(&time_source)), + .with_time_source(time_source.clone()), ) .endpoint_mode(EndpointMode::IpV6) .token_ttl(Duration::from_secs(600)) @@ -929,7 +934,7 @@ pub(crate) mod test { /// Verify that the end-to-end real client has a 1-second connect timeout #[tokio::test] - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] async fn one_second_connect_timeout() { use crate::imds::client::ImdsError; use aws_smithy_types::error::display::DisplayErrorContext; diff --git a/aws/rust-runtime/aws-config/src/imds/client/token.rs b/aws/rust-runtime/aws-config/src/imds/client/token.rs index a4de2c0deb..41e96777b4 100644 --- a/aws/rust-runtime/aws-config/src/imds/client/token.rs +++ b/aws/rust-runtime/aws-config/src/imds/client/token.rs @@ -17,9 +17,9 @@ use crate::imds::client::error::{ImdsError, TokenError, TokenErrorKind}; use crate::imds::client::ImdsResponseRetryClassifier; use aws_credential_types::cache::ExpiringCache; -use aws_credential_types::time_source::TimeSource; use aws_http::user_agent::UserAgentStage; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_async::time::SharedTimeSource; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::retry; use aws_smithy_http::body::SdkBody; @@ -65,7 +65,7 @@ pub(super) struct TokenMiddleware { client: Arc>>, token_parser: GetTokenResponseHandler, token: ExpiringCache, - time_source: TimeSource, + time_source: SharedTimeSource, endpoint: Uri, token_ttl: Duration, } @@ -79,12 +79,12 @@ impl Debug for TokenMiddleware { impl TokenMiddleware { pub(super) fn new( connector: DynConnector, - time_source: TimeSource, + time_source: SharedTimeSource, endpoint: Uri, token_ttl: Duration, retry_config: retry::Config, timeout_config: TimeoutConfig, - sleep_impl: Option>, + sleep_impl: Option, ) -> Self { let mut inner_builder = aws_smithy_client::Client::builder() .connector(connector) @@ -170,7 +170,7 @@ impl AsyncMapRequest for TokenMiddleware { #[derive(Clone)] struct GetTokenResponseHandler { - time: TimeSource, + time: SharedTimeSource, } impl ParseStrictResponse for GetTokenResponseHandler { @@ -197,4 +197,8 @@ impl ParseStrictResponse for GetTokenResponseHandler { expiry: self.time.now() + Duration::from_secs(ttl), }) } + + fn sensitive(&self) -> bool { + true + } } diff --git a/aws/rust-runtime/aws-config/src/imds/credentials.rs b/aws/rust-runtime/aws-config/src/imds/credentials.rs index 665d03f801..ccc65eaf99 100644 --- a/aws/rust-runtime/aws-config/src/imds/credentials.rs +++ b/aws/rust-runtime/aws-config/src/imds/credentials.rs @@ -14,8 +14,8 @@ use crate::imds::client::LazyClient; use crate::json_credentials::{parse_json_credentials, JsonCredentials, RefreshableCredentials}; use crate::provider_config::ProviderConfig; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; -use aws_credential_types::time_source::TimeSource; use aws_credential_types::Credentials; +use aws_smithy_async::time::SharedTimeSource; use aws_types::os_shim_internal::Env; use std::borrow::Cow; use std::error::Error as StdError; @@ -23,7 +23,10 @@ use std::fmt; use std::sync::{Arc, RwLock}; use std::time::{Duration, SystemTime}; -const CREDENTIAL_EXPIRATION_INTERVAL: Duration = Duration::from_secs(15 * 60); +const CREDENTIAL_EXPIRATION_INTERVAL: Duration = Duration::from_secs(10 * 60); +const WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY: &str = + "Attempting credential expiration extension due to a credential service availability issue. \ + A refresh of these credentials will be attempted again within the next"; #[derive(Debug)] struct ImdsCommunicationError { @@ -50,7 +53,7 @@ pub struct ImdsCredentialsProvider { client: LazyClient, env: Env, profile: Option, - time_source: TimeSource, + time_source: SharedTimeSource, last_retrieved_credentials: Arc>>, } @@ -192,25 +195,26 @@ impl ImdsCredentialsProvider { // // This allows continued use of the credentials even when IMDS returns expired ones. fn maybe_extend_expiration(&self, expiration: SystemTime) -> SystemTime { - let rng = fastrand::Rng::with_seed( - self.time_source - .now() - .duration_since(SystemTime::UNIX_EPOCH) + let now = self.time_source.now(); + // If credentials from IMDS are not stale, use them as they are. + if now < expiration { + return expiration; + } + + let mut rng = fastrand::Rng::with_seed( + now.duration_since(SystemTime::UNIX_EPOCH) .expect("now should be after UNIX EPOCH") .as_secs(), ); - // calculate credentials' refresh offset with jitter - let refresh_offset = - CREDENTIAL_EXPIRATION_INTERVAL + Duration::from_secs(rng.u64(120..=600)); - let new_expiry = self.time_source.now() + refresh_offset; - - if new_expiry < expiration { - return expiration; - } + // Calculate credentials' refresh offset with jitter, which should be less than 15 minutes + // the smallest amount of time credentials are valid for. + // Setting it to something longer than that may have the risk of the credentials expiring + // before the next refresh. + let refresh_offset = CREDENTIAL_EXPIRATION_INTERVAL + Duration::from_secs(rng.u64(0..=300)); + let new_expiry = now + refresh_offset; tracing::warn!( - "Attempting credential expiration extension due to a credential service availability issue. \ - A refresh of these credentials will be attempted again within the next {:.2} minutes.", + "{WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY} {:.2} minutes.", refresh_offset.as_secs_f64() / 60.0, ); @@ -297,11 +301,12 @@ mod test { use crate::imds::client::test::{ imds_request, imds_response, make_client, token_request, token_response, }; - use crate::imds::credentials::ImdsCredentialsProvider; + use crate::imds::credentials::{ + ImdsCredentialsProvider, WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY, + }; use crate::provider_config::ProviderConfig; use aws_credential_types::provider::ProvideCredentials; - use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; - use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::test_util::instant_time_and_sleep; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::TestConnection; use tracing_test::traced_test; @@ -342,6 +347,53 @@ mod test { connection.assert_requests_match(&[]); } + #[tokio::test] + #[traced_test] + async fn credentials_not_stale_should_be_used_as_they_are() { + let connection = TestConnection::new(vec![ + ( + token_request("http://169.254.169.254", 21600), + token_response(21600, TOKEN_A), + ), + ( + imds_request("http://169.254.169.254/latest/meta-data/iam/security-credentials/", TOKEN_A), + imds_response(r#"profile-name"#), + ), + ( + imds_request("http://169.254.169.254/latest/meta-data/iam/security-credentials/profile-name", TOKEN_A), + imds_response("{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-20T21:42:26Z\",\n \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"ASIARTEST\",\n \"SecretAccessKey\" : \"testsecret\",\n \"Token\" : \"testtoken\",\n \"Expiration\" : \"2021-09-21T04:16:53Z\"\n}"), + ), + ]); + + // set to 2021-09-21T04:16:50Z that makes returned credentials' expiry (2021-09-21T04:16:53Z) + // not stale + let time_of_request_to_fetch_credentials = UNIX_EPOCH + Duration::from_secs(1632197810); + let (time_source, sleep) = instant_time_and_sleep(time_of_request_to_fetch_credentials); + + let provider_config = ProviderConfig::no_configuration() + .with_http_connector(DynConnector::new(connection.clone())) + .with_sleep(sleep) + .with_time_source(time_source); + let client = crate::imds::Client::builder() + .configure(&provider_config) + .build() + .await + .expect("valid client"); + let provider = ImdsCredentialsProvider::builder() + .configure(&provider_config) + .imds_client(client) + .build(); + let creds = provider.provide_credentials().await.expect("valid creds"); + // The expiry should be equal to what is originally set (==2021-09-21T04:16:53Z). + assert_eq!( + creds.expiry(), + UNIX_EPOCH.checked_add(Duration::from_secs(1632197813)) + ); + connection.assert_requests_match(&[]); + + // There should not be logs indicating credentials are extended for stability. + assert!(!logs_contain(WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY)); + } #[tokio::test] #[traced_test] async fn expired_credentials_should_be_extended() { @@ -362,16 +414,12 @@ mod test { // set to 2021-09-21T17:41:25Z that renders fetched credentials already expired (2021-09-21T04:16:53Z) let time_of_request_to_fetch_credentials = UNIX_EPOCH + Duration::from_secs(1632246085); - let time_source = TimeSource::testing(&TestingTimeSource::new( - time_of_request_to_fetch_credentials, - )); - - tokio::time::pause(); + let (time_source, sleep) = instant_time_and_sleep(time_of_request_to_fetch_credentials); let provider_config = ProviderConfig::no_configuration() .with_http_connector(DynConnector::new(connection.clone())) - .with_time_source(time_source) - .with_sleep(TokioSleep::new()); + .with_sleep(sleep) + .with_time_source(time_source); let client = crate::imds::Client::builder() .configure(&provider_config) .build() @@ -386,11 +434,11 @@ mod test { connection.assert_requests_match(&[]); // We should inform customers that expired credentials are being used for stability. - assert!(logs_contain("Attempting credential expiration extension")); + assert!(logs_contain(WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY)); } #[tokio::test] - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] async fn read_timeout_during_credentials_refresh_should_yield_last_retrieved_credentials() { let client = crate::imds::Client::builder() // 240.* can never be resolved @@ -409,7 +457,7 @@ mod test { } #[tokio::test] - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] async fn read_timeout_during_credentials_refresh_should_error_without_last_retrieved_credentials( ) { let client = crate::imds::Client::builder() @@ -430,7 +478,7 @@ mod test { } #[tokio::test] - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] async fn external_timeout_during_credentials_refresh_should_yield_last_retrieved_credentials() { use aws_smithy_async::rt::sleep::AsyncSleep; let client = crate::imds::Client::builder() diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 88f55c4c3f..1deffdbe16 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -154,12 +154,14 @@ mod loader { use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{ProvideCredentials, SharedCredentialsProvider}; - use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; + use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep}; + use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; use aws_types::app_name::AppName; use aws_types::docs_for; + use aws_types::os_shim_internal::{Env, Fs}; use aws_types::SdkConfig; use crate::connector::default_connector; @@ -170,6 +172,17 @@ mod loader { use crate::profile::profile_file::ProfileFiles; use crate::provider_config::ProviderConfig; + #[derive(Default, Debug)] + enum CredentialsProviderOption { + /// No provider was set by the user. We can set up the default credentials provider chain. + #[default] + NotSet, + /// The credentials provider was explicitly unset. Do not set up a default chain. + ExplicitlyUnset, + /// Use the given credentials provider. + Set(SharedCredentialsProvider), + } + /// Load a cross-service [`SdkConfig`](aws_types::SdkConfig) from the environment /// /// This builder supports overriding individual components of the generated config. Overriding a component @@ -180,11 +193,11 @@ mod loader { pub struct ConfigLoader { app_name: Option, credentials_cache: Option, - credentials_provider: Option, + credentials_provider: CredentialsProviderOption, endpoint_url: Option, region: Option>, retry_config: Option, - sleep: Option>, + sleep: Option, timeout_config: Option, provider_config: Option, http_connector: Option, @@ -192,6 +205,9 @@ mod loader { profile_files_override: Option, use_fips: Option, use_dual_stack: Option, + time_source: Option, + env: Option, + fs: Option, } impl ConfigLoader { @@ -258,39 +274,53 @@ mod loader { /// is used to create timeout futures. pub fn sleep_impl(mut self, sleep: impl AsyncSleep + 'static) -> Self { // it's possible that we could wrapping an `Arc in an `Arc` and that's OK - self.sleep = Some(Arc::new(sleep)); + self.sleep = Some(SharedAsyncSleep::new(sleep)); self } - /// Override the [`HttpConnector`] for this [`ConfigLoader`]. The connector will be used when - /// sending operations. This **does not set** the HTTP connector used by config providers. - /// To change that connector, use [ConfigLoader::configure]. + /// Set the time source used for tasks like signing requests + pub fn time_source(mut self, time_source: impl TimeSource + 'static) -> Self { + self.time_source = Some(SharedTimeSource::new(time_source)); + self + } + + /// Override the [`HttpConnector`] for this [`ConfigLoader`]. The connector will be used for + /// both AWS services and credential providers. When [`HttpConnector::ConnectorFn`] is used, + /// the connector will be lazily instantiated as needed based on the provided settings. + /// + /// **Note**: In order to take advantage of late-configured timeout settings, you MUST use + /// [`HttpConnector::ConnectorFn`] + /// when configuring this connector. /// + /// If you wish to use a separate connector when creating clients, use the client-specific config. /// ## Examples /// ```no_run - /// # #[cfg(feature = "client-hyper")] + /// # use aws_smithy_async::rt::sleep::SharedAsyncSleep; + /// use aws_smithy_client::http_connector::HttpConnector; + /// #[cfg(feature = "client-hyper")] /// # async fn create_config() { /// use std::time::Duration; /// use aws_smithy_client::{Client, hyper_ext}; /// use aws_smithy_client::erase::DynConnector; /// use aws_smithy_client::http_connector::ConnectorSettings; /// - /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() - /// .with_webpki_roots() - /// .https_only() - /// .enable_http1() - /// .enable_http2() - /// .build(); - /// let smithy_connector = hyper_ext::Adapter::builder() - /// // Optionally set things like timeouts as well - /// .connector_settings( - /// ConnectorSettings::builder() - /// .connect_timeout(Duration::from_secs(5)) - /// .build() - /// ) - /// .build(https_connector); + /// let connector_fn = |settings: &ConnectorSettings, sleep: Option| { + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// // NOTE: setting `https_only()` will not allow this connector to work with IMDS. + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let mut smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings(settings.clone()); + /// smithy_connector.set_sleep_impl(sleep); + /// Some(DynConnector::new(smithy_connector.build(https_connector))) + /// }; + /// let connector = HttpConnector::ConnectorFn(std::sync::Arc::new(connector_fn)); /// let sdk_config = aws_config::from_env() - /// .http_connector(smithy_connector) + /// .http_connector(connector) /// .load() /// .await; /// # } @@ -340,7 +370,31 @@ mod loader { mut self, credentials_provider: impl ProvideCredentials + 'static, ) -> Self { - self.credentials_provider = Some(SharedCredentialsProvider::new(credentials_provider)); + self.credentials_provider = CredentialsProviderOption::Set( + SharedCredentialsProvider::new(credentials_provider), + ); + self + } + + /// Don't use credentials to sign requests. + /// + /// Turning off signing with credentials is necessary in some cases, such as using + /// anonymous auth for S3, calling operations in STS that don't require a signature, + /// or using token-based auth. + /// + /// # Examples + /// + /// Turn off credentials in order to call a service without signing: + /// ```no_run + /// # async fn create_config() { + /// let config = aws_config::from_env() + /// .no_credentials() + /// .load() + /// .await; + /// # } + /// ``` + pub fn no_credentials(mut self) -> Self { + self.credentials_provider = CredentialsProviderOption::ExplicitlyUnset; self } @@ -374,7 +428,7 @@ mod loader { /// /// # Example: Using a custom profile file path /// - /// ``` + /// ```no_run /// use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider}; /// use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind}; /// @@ -417,7 +471,7 @@ mod loader { /// /// # Example: Using a custom profile name /// - /// ``` + /// ```no_run /// use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider}; /// use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind}; /// @@ -487,6 +541,9 @@ mod loader { /// let shared_config = aws_config::from_env().configure(provider_config).load().await; /// # } /// ``` + #[deprecated( + note = "Use setters on this builder instead. configure is very hard to use correctly." + )] pub fn configure(mut self, provider_config: ProviderConfig) -> Self { self.provider_config = Some(provider_config); self @@ -502,9 +559,35 @@ mod loader { /// This means that if you provide a region provider that does not return a region, no region will /// be set in the resulting [`SdkConfig`](aws_types::SdkConfig) pub async fn load(self) -> SdkConfig { + let http_connector = self + .http_connector + .unwrap_or_else(|| HttpConnector::ConnectorFn(Arc::new(default_connector))); + + let time_source = self.time_source.unwrap_or_default(); + + let sleep_impl = if self.sleep.is_some() { + self.sleep + } else { + if default_async_sleep().is_none() { + tracing::warn!( + "An implementation of AsyncSleep was requested by calling default_async_sleep \ + but no default was set. + This happened when ConfigLoader::load was called during Config construction. \ + You can fix this by setting a sleep_impl on the ConfigLoader before calling \ + load or by enabling the rt-tokio feature" + ); + } + default_async_sleep() + }; + let conf = self .provider_config - .unwrap_or_default() + .unwrap_or_else(|| { + ProviderConfig::init(time_source.clone(), sleep_impl.clone()) + .with_fs(self.fs.unwrap_or_default()) + .with_env(self.env.unwrap_or_default()) + .with_http_connector(http_connector.clone()) + }) .with_profile_config(self.profile_files_override, self.profile_name_override); let region = if let Some(provider) = self.region { provider.region().await @@ -534,21 +617,6 @@ mod loader { .await }; - let sleep_impl = if self.sleep.is_some() { - self.sleep - } else { - if default_async_sleep().is_none() { - tracing::warn!( - "An implementation of AsyncSleep was requested by calling default_async_sleep \ - but no default was set. - This happened when ConfigLoader::load was called during Config construction. \ - You can fix this by setting a sleep_impl on the ConfigLoader before calling \ - load or by enabling the rt-tokio feature" - ); - } - default_async_sleep() - }; - let timeout_config = if let Some(timeout_config) = self.timeout_config { timeout_config } else { @@ -558,15 +626,27 @@ mod loader { .await }; - let http_connector = self - .http_connector - .unwrap_or_else(|| HttpConnector::ConnectorFn(Arc::new(default_connector))); + let credentials_provider = match self.credentials_provider { + CredentialsProviderOption::Set(provider) => Some(provider), + CredentialsProviderOption::NotSet => { + let mut builder = + credentials::DefaultCredentialsChain::builder().configure(conf.clone()); + builder.set_region(region.clone()); + Some(SharedCredentialsProvider::new(builder.build().await)) + } + CredentialsProviderOption::ExplicitlyUnset => None, + }; - let credentials_cache = self.credentials_cache.unwrap_or_else(|| { - let mut builder = CredentialsCache::lazy_builder().time_source(conf.time_source()); - builder.set_sleep(conf.sleep()); - builder.into_credentials_cache() - }); + let credentials_cache = if credentials_provider.is_some() { + Some(self.credentials_cache.unwrap_or_else(|| { + let mut builder = + CredentialsCache::lazy_builder().time_source(conf.time_source()); + builder.set_sleep(conf.sleep()); + builder.into_credentials_cache() + })) + } else { + None + }; let use_fips = if let Some(use_fips) = self.use_fips { Some(use_fips) @@ -580,23 +660,16 @@ mod loader { use_dual_stack_provider(&conf).await }; - let credentials_provider = if let Some(provider) = self.credentials_provider { - provider - } else { - let mut builder = credentials::DefaultCredentialsChain::builder().configure(conf); - builder.set_region(region.clone()); - SharedCredentialsProvider::new(builder.build().await) - }; - let mut builder = SdkConfig::builder() .region(region) .retry_config(retry_config) .timeout_config(timeout_config) - .credentials_cache(credentials_cache) - .credentials_provider(credentials_provider) + .time_source(time_source) .http_connector(http_connector); builder.set_app_name(app_name); + builder.set_credentials_cache(credentials_cache); + builder.set_credentials_provider(credentials_provider); builder.set_sleep_impl(sleep_impl); builder.set_endpoint_url(self.endpoint_url); builder.set_use_fips(use_fips); @@ -605,18 +678,35 @@ mod loader { } } + #[cfg(test)] + impl ConfigLoader { + pub(crate) fn env(mut self, env: Env) -> Self { + self.env = Some(env); + self + } + + pub(crate) fn fs(mut self, fs: Fs) -> Self { + self.fs = Some(fs); + self + } + } + #[cfg(test)] mod test { use aws_credential_types::provider::ProvideCredentials; use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::time::{StaticTimeSource, TimeSource}; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::never::NeverConnector; + use aws_smithy_client::test_connection::infallible_connection_fn; use aws_types::app_name::AppName; use aws_types::os_shim_internal::{Env, Fs}; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + use std::time::{SystemTime, UNIX_EPOCH}; use tracing_test::traced_test; use crate::profile::profile_file::{ProfileFileKind, ProfileFiles}; - use crate::provider_config::ProviderConfig; use crate::test_case::{no_traffic_connector, InstantSleep}; use crate::{from_env, ConfigLoader}; @@ -632,13 +722,10 @@ mod loader { let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk-ua-app-id = correct")]); let loader = from_env() - .configure( - ProviderConfig::empty() - .with_sleep(TokioSleep::new()) - .with_env(env) - .with_fs(fs) - .with_http_connector(DynConnector::new(NeverConnector::new())), - ) + .sleep_impl(TokioSleep::new()) + .env(env) + .fs(fs) + .http_connector(DynConnector::new(NeverConnector::new())) .profile_name("custom") .profile_files( ProfileFiles::builder() @@ -678,11 +765,9 @@ mod loader { } fn base_conf() -> ConfigLoader { - from_env().configure( - ProviderConfig::empty() - .with_sleep(InstantSleep) - .with_http_connector(no_traffic_connector()), - ) + from_env() + .sleep_impl(InstantSleep) + .http_connector(no_traffic_connector()) } #[tokio::test] @@ -706,5 +791,62 @@ mod loader { let conf = base_conf().app_name(app_name.clone()).load().await; assert_eq!(Some(&app_name), conf.app_name()); } + + #[cfg(all(not(aws_sdk_middleware_mode), feature = "rustls"))] + #[tokio::test] + async fn disable_default_credentials() { + let config = from_env().no_credentials().load().await; + assert!(config.credentials_cache().is_none()); + assert!(config.credentials_provider().is_none()); + } + + #[tokio::test] + async fn connector_is_shared() { + let num_requests = Arc::new(AtomicUsize::new(0)); + let movable = num_requests.clone(); + let conn = infallible_connection_fn(move |_req| { + movable.fetch_add(1, Ordering::Relaxed); + http::Response::new("ok!") + }); + let config = from_env().http_connector(conn.clone()).load().await; + config + .credentials_provider() + .unwrap() + .provide_credentials() + .await + .expect_err("no traffic is allowed"); + let num_requests = num_requests.load(Ordering::Relaxed); + assert!(num_requests > 0, "{}", num_requests); + } + + #[tokio::test] + async fn time_source_is_passed() { + #[derive(Debug)] + struct PanicTs; + impl TimeSource for PanicTs { + fn now(&self) -> SystemTime { + panic!("timesource-was-used") + } + } + let config = from_env() + .sleep_impl(InstantSleep) + .time_source(StaticTimeSource::new(UNIX_EPOCH)) + .http_connector(no_traffic_connector()) + .load() + .await; + // assert that the innards contain the customized fields + for inner in ["InstantSleep", "StaticTimeSource"] { + assert!( + format!("{:#?}", config.credentials_cache()).contains(inner), + "{:#?}", + config.credentials_cache() + ); + assert!( + format!("{:#?}", config.credentials_provider()).contains(inner), + "{:#?}", + config.credentials_cache() + ); + } + } } } diff --git a/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs b/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs index 193d9fb1ec..732d2b08fd 100644 --- a/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs +++ b/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs @@ -60,7 +60,7 @@ impl CredentialsProviderChain { } /// Add a fallback to the default provider chain - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] pub async fn or_default_provider(self) -> Self { self.or_else( "DefaultProviderChain", @@ -69,7 +69,7 @@ impl CredentialsProviderChain { } /// Creates a credential provider chain that starts with the default provider - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] pub async fn default_provider() -> Self { Self::first_try( "DefaultProviderChain", diff --git a/aws/rust-runtime/aws-config/src/profile/app_name.rs b/aws/rust-runtime/aws-config/src/profile/app_name.rs index 80a5f192ca..715681dadf 100644 --- a/aws/rust-runtime/aws-config/src/profile/app_name.rs +++ b/aws/rust-runtime/aws-config/src/profile/app_name.rs @@ -33,10 +33,14 @@ use aws_types::app_name::AppName; /// /// This provider is part of the [default app name provider chain](crate::default_provider::app_name). #[derive(Debug, Default)] +#[deprecated( + note = "This is unused and is deprecated for backwards compatibility. It will be removed in a future release." +)] pub struct ProfileFileAppNameProvider { provider_config: ProviderConfig, } +#[allow(deprecated)] impl ProfileFileAppNameProvider { /// Create a new [ProfileFileAppNameProvider} /// @@ -67,12 +71,14 @@ impl ProfileFileAppNameProvider { /// Builder for [ProfileFileAppNameProvider] #[derive(Debug, Default)] +#[allow(deprecated)] pub struct Builder { config: Option, profile_override: Option, profile_files: Option, } +#[allow(deprecated)] impl Builder { /// Override the configuration for this provider pub fn configure(mut self, config: &ProviderConfig) -> Self { @@ -87,6 +93,7 @@ impl Builder { } /// Build a [ProfileFileAppNameProvider] from this builder + #[allow(deprecated)] pub fn build(self) -> ProfileFileAppNameProvider { let conf = self .config @@ -97,77 +104,3 @@ impl Builder { } } } - -#[cfg(test)] -mod tests { - use super::ProfileFileAppNameProvider; - use crate::provider_config::ProviderConfig; - use crate::test_case::no_traffic_connector; - use aws_sdk_sts::config::AppName; - use aws_types::os_shim_internal::{Env, Fs}; - use tracing_test::traced_test; - - fn provider_config(config_contents: &str) -> ProviderConfig { - let fs = Fs::from_slice(&[("test_config", config_contents)]); - let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]); - ProviderConfig::empty() - .with_fs(fs) - .with_env(env) - .with_http_connector(no_traffic_connector()) - } - - fn default_provider(config_contents: &str) -> ProfileFileAppNameProvider { - ProfileFileAppNameProvider::builder() - .configure(&provider_config(config_contents)) - .build() - } - - #[tokio::test] - async fn no_app_name() { - assert_eq!(None, default_provider("[default]\n").app_name().await); - } - - #[tokio::test] - async fn app_name_default_profile() { - assert_eq!( - Some(AppName::new("test").unwrap()), - default_provider("[default]\nsdk-ua-app-id = test") - .app_name() - .await - ); - } - - #[tokio::test] - async fn app_name_other_profiles() { - let config = "\ - [default]\n\ - sdk-ua-app-id = test\n\ - \n\ - [profile other]\n\ - sdk-ua-app-id = bar\n - "; - assert_eq!( - Some(AppName::new("bar").unwrap()), - ProfileFileAppNameProvider::builder() - .profile_name("other") - .configure(&provider_config(config)) - .build() - .app_name() - .await - ); - } - - #[traced_test] - #[tokio::test] - async fn invalid_app_name() { - assert_eq!( - None, - default_provider("[default]\nsdk-ua-app-id = definitely invalid") - .app_name() - .await - ); - assert!(logs_contain( - "`sdk-ua-app-id` property `definitely invalid` was invalid" - )); - } -} diff --git a/aws/rust-runtime/aws-config/src/profile/credentials.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs index 9ce085503b..c3d08e58d5 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -23,12 +23,13 @@ //! through a series of providers. use crate::profile::credentials::exec::named::NamedProviderFactory; -use crate::profile::credentials::exec::{ClientConfiguration, ProviderChain}; +use crate::profile::credentials::exec::ProviderChain; use crate::profile::parser::ProfileFileLoadError; use crate::profile::profile_file::ProfileFiles; use crate::profile::Profile; use crate::provider_config::ProviderConfig; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; +use aws_sdk_sts::config::Builder as StsConfigBuilder; use aws_smithy_types::error::display::DisplayErrorContext; use std::borrow::Cow; use std::collections::HashMap; @@ -97,7 +98,7 @@ impl ProvideCredentials for ProfileFileCredentialsProvider { /// future::ProvideCredentials::new(self.load_credentials()) /// } /// } -/// # if cfg!(any(feature = "rustls", feature = "native-tls")) { +/// # if cfg!(feature = "rustls") { /// let provider = ProfileFileCredentialsProvider::builder() /// .with_custom_provider("Custom", MyCustomProvider) /// .build(); @@ -141,7 +142,7 @@ impl ProvideCredentials for ProfileFileCredentialsProvider { #[derive(Debug)] pub struct ProfileFileCredentialsProvider { factory: NamedProviderFactory, - client_config: ClientConfiguration, + sts_config: StsConfigBuilder, provider_config: ProviderConfig, } @@ -181,7 +182,7 @@ impl ProfileFileCredentialsProvider { }; for provider in inner_provider.chain().iter() { let next_creds = provider - .credentials(creds, &self.client_config) + .credentials(creds, &self.sts_config) .instrument(tracing::debug_span!("load_assume_role", provider = ?provider)) .await; match next_creds { @@ -257,6 +258,13 @@ pub enum ProfileFileError { /// The name of the provider name: String, }, + + /// Feature not enabled + #[non_exhaustive] + FeatureNotEnabled { + /// The feature or comma delimited list of features that must be enabled + feature: Cow<'static, str>, + }, } impl ProfileFileError { @@ -309,6 +317,12 @@ impl Display for ProfileFileError { "profile `{}` did not contain credential information", profile ), + ProfileFileError::FeatureNotEnabled { feature: message } => { + write!( + f, + "This behavior requires following cargo feature(s) enabled: {message}", + ) + } } } } @@ -362,7 +376,7 @@ impl Builder { /// } /// } /// - /// # if cfg!(any(feature = "rustls", feature = "native-tls")) { + /// # if cfg!(feature = "rustls") { /// let provider = ProfileFileCredentialsProvider::builder() /// .with_custom_provider("Custom", MyCustomProvider) /// .build(); @@ -427,14 +441,10 @@ impl Builder { ) }); let factory = exec::named::NamedProviderFactory::new(named_providers); - let core_client = conf.sts_client(); ProfileFileCredentialsProvider { factory, - client_config: ClientConfiguration { - sts_client: core_client, - region: conf.region(), - }, + sts_config: conf.sts_client_config(), provider_config: conf, } } @@ -455,14 +465,11 @@ async fn build_provider_chain( #[cfg(test)] mod test { - use tracing_test::traced_test; - use crate::profile::credentials::Builder; use crate::test_case::TestEnvironment; macro_rules! make_test { ($name: ident) => { - #[traced_test] #[tokio::test] async fn $name() { TestEnvironment::from_dir(concat!( diff --git a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs index 351e7df8d8..838007ad1e 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs @@ -8,15 +8,13 @@ use crate::credential_process::CredentialProcessProvider; use crate::profile::credentials::ProfileFileError; use crate::provider_config::ProviderConfig; #[cfg(feature = "credentials-sso")] -use crate::sso::{SsoConfig, SsoCredentialsProvider}; +use crate::sso::{SsoCredentialsProvider, SsoProviderConfig}; use crate::sts; use crate::web_identity_token::{StaticConfiguration, WebIdentityTokenCredentialsProvider}; use aws_credential_types::provider::{self, error::CredentialsError, ProvideCredentials}; -use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_sdk_sts::operation::assume_role::AssumeRoleInput; -use aws_sdk_sts::{config::Credentials, Config}; -use aws_smithy_client::erase::DynConnector; -use aws_types::region::Region; +use aws_sdk_sts::config::{Builder as StsConfigBuilder, Credentials}; +use aws_sdk_sts::Client as StsClient; +use aws_smithy_async::time::SharedTimeSource; use std::fmt::Debug; use std::sync::Arc; @@ -25,41 +23,29 @@ pub(super) struct AssumeRoleProvider { role_arn: String, external_id: Option, session_name: Option, -} - -#[derive(Debug)] -pub(super) struct ClientConfiguration { - pub(super) sts_client: aws_smithy_client::Client, - pub(super) region: Option, + time_source: SharedTimeSource, } impl AssumeRoleProvider { pub(super) async fn credentials( &self, input_credentials: Credentials, - client_config: &ClientConfiguration, + sts_config: &StsConfigBuilder, ) -> provider::Result { - let config = Config::builder() + let config = sts_config + .clone() .credentials_provider(input_credentials) - .region(client_config.region.clone()) .build(); - let session_name = &self - .session_name - .as_ref() - .cloned() - .unwrap_or_else(|| sts::util::default_session_name("assume-role-from-profile")); - let operation = AssumeRoleInput::builder() + let client = StsClient::from_conf(config); + let session_name = &self.session_name.as_ref().cloned().unwrap_or_else(|| { + sts::util::default_session_name("assume-role-from-profile", self.time_source.now()) + }); + let assume_role_creds = client + .assume_role() .role_arn(&self.role_arn) .set_external_id(self.external_id.clone()) .role_session_name(session_name) - .build() - .expect("operation is valid") - .make_operation(&config) - .await - .expect("valid operation"); - let assume_role_creds = client_config - .sts_client - .call(operation) + .send() .await .map_err(CredentialsError::provider_error)? .credentials; @@ -111,7 +97,12 @@ impl ProviderChain { web_identity_token_file: web_identity_token_file.into(), role_arn: role_arn.to_string(), session_name: session_name.map(|sess| sess.to_string()).unwrap_or_else( - || sts::util::default_session_name("web-identity-token-profile"), + || { + sts::util::default_session_name( + "web-identity-token-profile", + provider_config.time_source().now(), + ) + }, ), }) .configure(provider_config) @@ -127,7 +118,8 @@ impl ProviderChain { } => { #[cfg(feature = "credentials-sso")] { - let sso_config = SsoConfig { + use aws_types::region::Region; + let sso_config = SsoProviderConfig { account_id: sso_account_id.to_string(), role_name: sso_role_name.to_string(), start_url: sso_start_url.to_string(), @@ -137,8 +129,8 @@ impl ProviderChain { } #[cfg(not(feature = "credentials-sso"))] { - Err(ProfileFileError::UnknownProvider { - name: "sso".to_string(), + Err(ProfileFileError::FeatureNotEnabled { + feature: "credentials-sso".into(), })? } } @@ -153,6 +145,7 @@ impl ProviderChain { role_arn: role_arn.role_arn.into(), external_id: role_arn.external_id.map(|id| id.into()), session_name: role_arn.session_name.map(|id| id.into()), + time_source: provider_config.time_source(), } }) .collect(); diff --git a/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs b/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs index 36b469b729..5b3b5c1ea8 100644 --- a/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs @@ -113,7 +113,7 @@ pub(super) fn merge_in( } } -fn merge_into_base<'a>(target: &mut Profile, profile: HashMap<&str, Cow<'a, str>>) { +fn merge_into_base(target: &mut Profile, profile: HashMap<&str, Cow<'_, str>>) { for (k, v) in profile { match validate_identifier(k) { Ok(k) => { diff --git a/aws/rust-runtime/aws-config/src/profile/profile_file.rs b/aws/rust-runtime/aws-config/src/profile/profile_file.rs index a90472c8e6..dfe9eb33ca 100644 --- a/aws/rust-runtime/aws-config/src/profile/profile_file.rs +++ b/aws/rust-runtime/aws-config/src/profile/profile_file.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; /// /// # Example: Using a custom profile file path /// -/// ``` +/// ```no_run /// use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider}; /// use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind}; /// use std::sync::Arc; diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index cb4bd5aa17..f1caa75e51 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -5,8 +5,8 @@ //! Configuration Options for Credential Providers -use aws_credential_types::time_source::TimeSource; -use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; +use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep}; +use aws_smithy_async::time::SharedTimeSource; use aws_smithy_client::erase::DynConnector; use aws_smithy_types::error::display::DisplayErrorContext; use aws_types::os_shim_internal::{Env, Fs}; @@ -38,9 +38,9 @@ use crate::profile::{ProfileFileLoadError, ProfileSet}; pub struct ProviderConfig { env: Env, fs: Fs, - time_source: TimeSource, + time_source: SharedTimeSource, connector: HttpConnector, - sleep: Option>, + sleep: Option, region: Option, /// An AWS profile created from `ProfileFiles` and a `profile_name` parsed_profile: Arc>>, @@ -64,7 +64,7 @@ impl Debug for ProviderConfig { impl Default for ProviderConfig { fn default() -> Self { let connector = HttpConnector::ConnectorFn(Arc::new( - |settings: &ConnectorSettings, sleep: Option>| { + |settings: &ConnectorSettings, sleep: Option| { default_connector(settings, sleep) }, )); @@ -72,7 +72,7 @@ impl Default for ProviderConfig { Self { env: Env::default(), fs: Fs::default(), - time_source: TimeSource::default(), + time_source: SharedTimeSource::default(), connector, sleep: default_async_sleep(), region: None, @@ -90,7 +90,7 @@ impl ProviderConfig { /// Unlike [`ProviderConfig::empty`] where `env` and `fs` will use their non-mocked implementations, /// this method will use an empty mock environment and an empty mock file system. pub fn no_configuration() -> Self { - use aws_credential_types::time_source::TestingTimeSource; + use aws_smithy_async::time::StaticTimeSource; use std::collections::HashMap; use std::time::UNIX_EPOCH; let fs = Fs::from_raw_map(HashMap::new()); @@ -100,7 +100,7 @@ impl ProviderConfig { profile_files: ProfileFiles::default(), env, fs, - time_source: TimeSource::testing(&TestingTimeSource::new(UNIX_EPOCH)), + time_source: SharedTimeSource::new(StaticTimeSource::new(UNIX_EPOCH)), connector: HttpConnector::Prebuilt(None), sleep: None, region: None, @@ -121,7 +121,7 @@ impl ProviderConfig { /// /// # Examples /// ```no_run - /// # #[cfg(any(feature = "rustls", feature = "native-tls"))] + /// # #[cfg(feature = "rustls")] /// # fn example() { /// use aws_config::provider_config::ProviderConfig; /// use aws_sdk_sts::config::Region; @@ -140,7 +140,7 @@ impl ProviderConfig { ProviderConfig { env: Env::default(), fs: Fs::default(), - time_source: TimeSource::default(), + time_source: SharedTimeSource::default(), connector: HttpConnector::Prebuilt(None), sleep: None, region: None, @@ -150,6 +150,21 @@ impl ProviderConfig { } } + /// Initializer for ConfigBag to avoid possibly setting incorrect defaults. + pub(crate) fn init(time_source: SharedTimeSource, sleep: Option) -> Self { + Self { + parsed_profile: Default::default(), + profile_files: ProfileFiles::default(), + env: Env::default(), + fs: Fs::default(), + time_source, + connector: HttpConnector::Prebuilt(None), + sleep, + region: None, + profile_name_override: None, + } + } + /// Create a default provider config with the region region automatically loaded from the default chain. /// /// # Examples @@ -179,7 +194,7 @@ impl ProviderConfig { } #[allow(dead_code)] - pub(crate) fn time_source(&self) -> TimeSource { + pub(crate) fn time_source(&self) -> SharedTimeSource { self.time_source.clone() } @@ -195,7 +210,7 @@ impl ProviderConfig { } #[allow(dead_code)] - pub(crate) fn sleep(&self) -> Option> { + pub(crate) fn sleep(&self) -> Option { self.sleep.clone() } @@ -270,10 +285,7 @@ impl ProviderConfig { self.with_region(provider_chain.region().await) } - // these setters are doc(hidden) because they only exist for tests - - #[doc(hidden)] - pub fn with_fs(self, fs: Fs) -> Self { + pub(crate) fn with_fs(self, fs: Fs) -> Self { ProviderConfig { parsed_profile: Default::default(), fs, @@ -281,8 +293,7 @@ impl ProviderConfig { } } - #[doc(hidden)] - pub fn with_env(self, env: Env) -> Self { + pub(crate) fn with_env(self, env: Env) -> Self { ProviderConfig { parsed_profile: Default::default(), env, @@ -290,24 +301,24 @@ impl ProviderConfig { } } - #[doc(hidden)] - pub fn with_time_source(self, time_source: TimeSource) -> Self { + /// Override the time source for this configuration + pub fn with_time_source( + self, + time_source: impl aws_smithy_async::time::TimeSource + 'static, + ) -> Self { ProviderConfig { - time_source, + time_source: SharedTimeSource::new(time_source), ..self } } /// Override the HTTPS connector for this configuration /// - /// **Warning**: Use of this method will prevent you from taking advantage of the HTTP connect timeouts. - /// Consider [`ProviderConfig::with_tcp_connector`]. - /// - /// # Stability - /// This method is expected to change to support HTTP configuration. - pub fn with_http_connector(self, connector: DynConnector) -> Self { + /// **Note**: In order to take advantage of late-configured timeout settings, use [`HttpConnector::ConnectorFn`] + /// when configuring this connector. + pub fn with_http_connector(self, connector: impl Into) -> Self { ProviderConfig { - connector: HttpConnector::Prebuilt(Some(connector)), + connector: connector.into(), ..self } } @@ -332,8 +343,7 @@ impl ProviderConfig { C::Future: Unpin + Send + 'static, C::Error: Into>, { - let connector_fn = move |settings: &ConnectorSettings, - sleep: Option>| { + let connector_fn = move |settings: &ConnectorSettings, sleep: Option| { let mut builder = aws_smithy_client::hyper_ext::Adapter::builder() .connector_settings(settings.clone()); if let Some(sleep) = sleep { @@ -350,7 +360,7 @@ impl ProviderConfig { /// Override the sleep implementation for this configuration pub fn with_sleep(self, sleep: impl AsyncSleep + 'static) -> Self { ProviderConfig { - sleep: Some(Arc::new(sleep)), + sleep: Some(SharedAsyncSleep::new(sleep)), ..self } } diff --git a/aws/rust-runtime/aws-config/src/sso.rs b/aws/rust-runtime/aws-config/src/sso.rs index 7c693bdf2e..592ed5b6bc 100644 --- a/aws/rust-runtime/aws-config/src/sso.rs +++ b/aws/rust-runtime/aws-config/src/sso.rs @@ -14,12 +14,11 @@ use crate::fs_util::{home_dir, Os}; use crate::json_credentials::{json_parse_loop, InvalidJsonCredentials}; use crate::provider_config::ProviderConfig; +use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::Credentials; -use aws_sdk_sso::middleware::DefaultMiddleware as SsoMiddleware; -use aws_sdk_sso::operation::get_role_credentials::GetRoleCredentialsInput; use aws_sdk_sso::types::RoleCredentials; -use aws_smithy_client::erase::DynConnector; +use aws_sdk_sso::{config::Builder as SsoConfigBuilder, Client as SsoClient, Config as SsoConfig}; use aws_smithy_json::deserialize::Token; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; @@ -32,23 +31,11 @@ use std::fmt::{Display, Formatter}; use std::io; use std::path::PathBuf; +use crate::connector::expect_connector; +use aws_smithy_types::retry::RetryConfig; use ring::digest; use zeroize::Zeroizing; -impl crate::provider_config::ProviderConfig { - pub(crate) fn sso_client( - &self, - ) -> aws_smithy_client::Client { - use crate::connector::expect_connector; - - let mut client_builder = aws_smithy_client::Client::builder() - .connector(expect_connector(self.connector(&Default::default()))) - .middleware(SsoMiddleware::default()); - client_builder.set_sleep_impl(self.sleep()); - client_builder.build() - } -} - /// SSO Credentials Provider /// /// _Note: This provider is part of the default credentials chain and is integrated with the profile-file provider._ @@ -59,8 +46,8 @@ impl crate::provider_config::ProviderConfig { pub struct SsoCredentialsProvider { fs: Fs, env: Env, - sso_config: SsoConfig, - client: aws_smithy_client::Client, + sso_provider_config: SsoProviderConfig, + sso_config: SsoConfigBuilder, } impl SsoCredentialsProvider { @@ -69,20 +56,37 @@ impl SsoCredentialsProvider { Builder::new() } - pub(crate) fn new(provider_config: &ProviderConfig, sso_config: SsoConfig) -> Self { + pub(crate) fn new( + provider_config: &ProviderConfig, + sso_provider_config: SsoProviderConfig, + ) -> Self { let fs = provider_config.fs(); let env = provider_config.env(); + let mut sso_config = SsoConfig::builder() + .http_connector(expect_connector( + "The SSO credentials provider", + provider_config.connector(&Default::default()), + )) + .retry_config(RetryConfig::standard()); + sso_config.set_sleep_impl(provider_config.sleep()); + SsoCredentialsProvider { fs, env, - client: provider_config.sso_client(), + sso_provider_config, sso_config, } } async fn credentials(&self) -> provider::Result { - load_sso_credentials(&self.sso_config, &self.client, &self.env, &self.fs).await + load_sso_credentials( + &self.sso_provider_config, + &self.sso_config, + &self.env, + &self.fs, + ) + .await } } @@ -151,7 +155,7 @@ impl Builder { /// - [`region`](Self::region) pub fn build(self) -> SsoCredentialsProvider { let provider_config = self.provider_config.unwrap_or_default(); - let sso_config = SsoConfig { + let sso_config = SsoProviderConfig { account_id: self.account_id.expect("account_id must be set"), role_name: self.role_name.expect("role_name must be set"), start_url: self.start_url.expect("start_url must be set"), @@ -193,7 +197,7 @@ impl Error for LoadTokenError { } #[derive(Debug)] -pub(crate) struct SsoConfig { +pub(crate) struct SsoProviderConfig { pub(crate) account_id: String, pub(crate) role_name: String, pub(crate) start_url: String, @@ -201,30 +205,27 @@ pub(crate) struct SsoConfig { } async fn load_sso_credentials( - sso_config: &SsoConfig, - sso: &aws_smithy_client::Client, + sso_provider_config: &SsoProviderConfig, + sso_config: &SsoConfigBuilder, env: &Env, fs: &Fs, ) -> provider::Result { - let token = load_token(&sso_config.start_url, env, fs) + let token = load_token(&sso_provider_config.start_url, env, fs) .await .map_err(CredentialsError::provider_error)?; - let config = aws_sdk_sso::Config::builder() - .region(sso_config.region.clone()) + let config = sso_config + .clone() + .region(sso_provider_config.region.clone()) + .credentials_cache(CredentialsCache::no_caching()) .build(); - let operation = GetRoleCredentialsInput::builder() - .role_name(&sso_config.role_name) + // TODO(enableNewSmithyRuntimeCleanup): Use `customize().config_override()` to set the region instead of creating a new client once middleware is removed + let client = SsoClient::from_conf(config); + let resp = client + .get_role_credentials() + .role_name(&sso_provider_config.role_name) .access_token(&*token.access_token) - .account_id(&sso_config.account_id) - .build() - .map_err(|err| { - CredentialsError::unhandled(format!("could not construct SSO token input: {}", err)) - })? - .make_operation(&config) - .await - .map_err(CredentialsError::unhandled)?; - let resp = sso - .call(operation) + .account_id(&sso_provider_config.account_id) + .send() .await .map_err(CredentialsError::provider_error)?; let credentials: RoleCredentials = resp diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs index e6e02a43cb..028409bfbe 100644 --- a/aws/rust-runtime/aws-config/src/sts.rs +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -5,23 +5,27 @@ //! Credential provider augmentation through the AWS Security Token Service (STS). -use crate::connector::expect_connector; -use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_smithy_client::erase::DynConnector; -use aws_smithy_client::Client; - pub(crate) mod util; pub use assume_role::{AssumeRoleProvider, AssumeRoleProviderBuilder}; mod assume_role; +use crate::connector::expect_connector; +use aws_sdk_sts::config::Builder as StsConfigBuilder; +use aws_smithy_types::retry::RetryConfig; + impl crate::provider_config::ProviderConfig { - pub(crate) fn sts_client(&self) -> Client { - let mut builder = Client::builder() - .connector(expect_connector(self.connector(&Default::default()))) - .middleware(DefaultMiddleware::default()); + pub(crate) fn sts_client_config(&self) -> StsConfigBuilder { + let mut builder = aws_sdk_sts::Config::builder() + .http_connector(expect_connector( + "The STS features of aws-config", + self.connector(&Default::default()), + )) + .retry_config(RetryConfig::standard()) + .region(self.region()) + .time_source(self.time_source()); builder.set_sleep_impl(self.sleep()); - builder.build() + builder } } diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 35f2b3fa27..90cd91195f 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -5,12 +5,14 @@ //! Assume credentials for a role through the AWS Security Token Service (STS). +use crate::connector::expect_connector; use crate::provider_config::ProviderConfig; use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; -use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_sdk_sts::operation::assume_role::{AssumeRoleError, AssumeRoleInput}; +use aws_sdk_sts::operation::assume_role::builders::AssumeRoleFluentBuilder; +use aws_sdk_sts::operation::assume_role::AssumeRoleError; use aws_sdk_sts::types::PolicyDescriptorType; +use aws_sdk_sts::Client as StsClient; use aws_smithy_client::erase::DynConnector; use aws_smithy_http::result::SdkError; use aws_smithy_types::error::display::DisplayErrorContext; @@ -46,9 +48,7 @@ pub struct AssumeRoleProvider { #[derive(Debug)] struct Inner { - sts: aws_smithy_client::Client, - conf: aws_sdk_sts::Config, - op: AssumeRoleInput, + fluent_builder: AssumeRoleFluentBuilder, } impl AssumeRoleProvider { @@ -179,7 +179,13 @@ impl AssumeRoleProviderBuilder { self } + #[deprecated( + note = "This should not be necessary as the default, no caching, is usually what you want." + )] /// Set the [`CredentialsCache`] for credentials retrieved from STS. + /// + /// By default, an [`AssumeRoleProvider`] internally uses `NoCredentialsCache` because the + /// provider itself will be wrapped by `LazyCredentialsCache` when a service client is created. pub fn credentials_cache(mut self, cache: CredentialsCache) -> Self { self.credentials_cache = Some(cache); self @@ -198,47 +204,37 @@ impl AssumeRoleProviderBuilder { pub fn build(self, provider: impl ProvideCredentials + 'static) -> AssumeRoleProvider { let conf = self.conf.unwrap_or_default(); - let credentials_cache = self.credentials_cache.unwrap_or_else(|| { - let mut builder = CredentialsCache::lazy_builder().time_source(conf.time_source()); - builder.set_sleep(conf.sleep()); - builder.into_credentials_cache() - }); + let credentials_cache = self + .credentials_cache + .unwrap_or_else(CredentialsCache::no_caching); - let config = aws_sdk_sts::Config::builder() + let mut config = aws_sdk_sts::Config::builder() .credentials_cache(credentials_cache) .credentials_provider(provider) + .time_source(conf.time_source()) .region(self.region.clone()) - .build(); - - let conn = conf - .connector(&Default::default()) - .expect("A connector must be provided"); - let mut client_builder = aws_smithy_client::Client::builder() - .connector(conn) - .middleware(DefaultMiddleware::new()); - client_builder.set_sleep_impl(conf.sleep()); - let client = client_builder.build(); - - let session_name = self - .session_name - .unwrap_or_else(|| super::util::default_session_name("assume-role-provider")); - - let operation = AssumeRoleInput::builder() + .http_connector(expect_connector( + "The AssumeRole credentials provider", + conf.connector(&Default::default()), + )); + config.set_sleep_impl(conf.sleep()); + + let session_name = self.session_name.unwrap_or_else(|| { + super::util::default_session_name("assume-role-provider", conf.time_source().now()) + }); + + let sts_client = StsClient::from_conf(config.build()); + let fluent_builder = sts_client + .assume_role() .set_role_arn(Some(self.role_arn)) .set_external_id(self.external_id) .set_role_session_name(Some(session_name)) .set_policy(self.policy) .set_policy_arns(self.policy_arns) - .set_duration_seconds(self.session_length.map(|dur| dur.as_secs() as i32)) - .build() - .expect("operation is valid"); + .set_duration_seconds(self.session_length.map(|dur| dur.as_secs() as i32)); AssumeRoleProvider { - inner: Inner { - sts: client, - conf: config, - op: operation, - }, + inner: Inner { fluent_builder }, } } } @@ -246,14 +242,8 @@ impl AssumeRoleProviderBuilder { impl Inner { async fn credentials(&self) -> provider::Result { tracing::debug!("retrieving assumed credentials"); - let op = self - .op - .clone() - .make_operation(&self.conf) - .await - .expect("valid operation"); - let assumed = self.sts.call(op).in_current_span().await; + let assumed = self.fluent_builder.clone().send().in_current_span().await; match assumed { Ok(assumed) => { tracing::debug!( @@ -301,9 +291,10 @@ mod test { use crate::sts::AssumeRoleProvider; use aws_credential_types::credential_fn::provide_credentials_fn; use aws_credential_types::provider::ProvideCredentials; - use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; use aws_credential_types::Credentials; use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::test_util::instant_time_and_sleep; + use aws_smithy_async::time::StaticTimeSource; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::{capture_request, TestConnection}; use aws_smithy_http::body::SdkBody; @@ -315,9 +306,9 @@ mod test { let (server, request) = capture_request(None); let provider_conf = ProviderConfig::empty() .with_sleep(TokioSleep::new()) - .with_time_source(TimeSource::testing(&TestingTimeSource::new( + .with_time_source(StaticTimeSource::new( UNIX_EPOCH + Duration::from_secs(1234567890 - 120), - ))) + )) .with_http_connector(DynConnector::new(server)); let provider = AssumeRoleProvider::builder("myrole") .configure(&provider_conf) @@ -333,39 +324,69 @@ mod test { } #[tokio::test] - async fn provider_caches_credentials() { + async fn provider_does_not_cache_credentials_by_default() { let conn = TestConnection::new(vec![ (http::Request::new(SdkBody::from("request body")), http::Response::builder().status(200).body(SdkBody::from( - "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/imds-chained-role-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n secretkeycorrect\n tokencorrect\n 2009-02-13T23:31:30Z\n \n \n \n d9d47248-fd55-4686-ad7c-0fb7cd1cddd7\n \n\n" + "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/assume-provider-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n secretkeycorrect\n tokencorrect\n 2009-02-13T23:31:30Z\n \n \n \n d9d47248-fd55-4686-ad7c-0fb7cd1cddd7\n \n\n" )).unwrap()), (http::Request::new(SdkBody::from("request body")), http::Response::builder().status(200).body(SdkBody::from( - "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/imds-chained-role-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n secretkeycorrect\n tokencorrect\n 2009-02-13T23:31:30Z\n \n \n \n d9d47248-fd55-4686-ad7c-0fb7cd1cddd7\n \n\n" + "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/assume-provider-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n TESTSECRET\n tokencorrect\n 2009-02-13T23:33:30Z\n \n \n \n c2e971c2-702d-4124-9b1f-1670febbea18\n \n\n" )).unwrap()), ]); + + let (testing_time_source, sleep) = instant_time_and_sleep( + UNIX_EPOCH + Duration::from_secs(1234567890 - 120), // 1234567890 since UNIX_EPOCH is 2009-02-13T23:31:30Z + ); + let provider_conf = ProviderConfig::empty() - .with_sleep(TokioSleep::new()) - .with_time_source(TimeSource::testing(&TestingTimeSource::new( - UNIX_EPOCH + Duration::from_secs(1234567890 - 120), - ))) + .with_sleep(sleep) + .with_time_source(testing_time_source.clone()) .with_http_connector(DynConnector::new(conn)); + let credentials_list = std::sync::Arc::new(std::sync::Mutex::new(vec![ + Credentials::new( + "test", + "test", + None, + Some(UNIX_EPOCH + Duration::from_secs(1234567890 + 1)), + "test", + ), + Credentials::new( + "test", + "test", + None, + Some(UNIX_EPOCH + Duration::from_secs(1234567890 + 120)), + "test", + ), + ])); + let credentials_list_cloned = credentials_list.clone(); let provider = AssumeRoleProvider::builder("myrole") .configure(&provider_conf) .region(Region::new("us-east-1")) - .build(provide_credentials_fn(|| async { - Ok(Credentials::for_tests()) + .build(provide_credentials_fn(move || { + let list = credentials_list.clone(); + async move { + let next = list.lock().unwrap().remove(0); + Ok(next) + } })); + let creds_first = provider .provide_credentials() .await .expect("should return valid credentials"); - // The effect of caching is implicitly enabled by a `LazyCredentialsCache` - // baked in the configuration for STS stored in `provider.inner.conf`. + + // After time has been advanced by 120 seconds, the first credentials _could_ still be valid + // if `LazyCredentialsCache` were used, but the provider uses `NoCredentialsCache` by default + // so the first credentials will not be used. + testing_time_source.advance(Duration::from_secs(120)); + let creds_second = provider .provide_credentials() .await - .expect("cached credentials should be returned"); - assert_eq!(creds_first, creds_second); + .expect("should return the second credentials"); + assert_ne!(creds_first, creds_second); + assert!(credentials_list_cloned.lock().unwrap().is_empty()); } } diff --git a/aws/rust-runtime/aws-config/src/sts/util.rs b/aws/rust-runtime/aws-config/src/sts/util.rs index 426d3eb40a..bc6151985d 100644 --- a/aws/rust-runtime/aws-config/src/sts/util.rs +++ b/aws/rust-runtime/aws-config/src/sts/util.rs @@ -45,9 +45,7 @@ pub(crate) fn into_credentials( /// STS Assume Role providers MUST assign a name to their generated session. When a user does not /// provide a name for the session, the provider will choose a name composed of a base + a timestamp, /// e.g. `profile-file-provider-123456789` -pub(crate) fn default_session_name(base: &str) -> String { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("post epoch"); +pub(crate) fn default_session_name(base: &str, ts: SystemTime) -> String { + let now = ts.duration_since(UNIX_EPOCH).expect("post epoch"); format!("{}-{}", base, now.as_millis()) } diff --git a/aws/rust-runtime/aws-config/src/test_case.rs b/aws/rust-runtime/aws-config/src/test_case.rs index 38593fcb46..14859d57ef 100644 --- a/aws/rust-runtime/aws-config/src/test_case.rs +++ b/aws/rust-runtime/aws-config/src/test_case.rs @@ -16,11 +16,17 @@ use serde::Deserialize; use crate::connector::default_connector; use aws_smithy_types::error::display::DisplayErrorContext; use std::collections::HashMap; +use std::env; use std::error::Error; use std::fmt::Debug; use std::future::Future; +use std::io::Write; use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex}; use std::time::{Duration, UNIX_EPOCH}; +use tracing::dispatcher::DefaultGuard; +use tracing::Level; +use tracing_subscriber::fmt::TestWriter; /// Test case credentials /// @@ -84,7 +90,7 @@ impl AsyncSleep for InstantSleep { } } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub(crate) enum GenericTestResult { Ok(T), ErrorContains(String), @@ -109,7 +115,8 @@ where } (Err(actual_error), GenericTestResult::Ok(expected_creds)) => panic!( "expected credentials ({:?}) but an error was returned: {}", - expected_creds, actual_error + expected_creds, + DisplayErrorContext(&actual_error) ), (Ok(creds), GenericTestResult::ErrorContains(substr)) => panic!( "expected an error containing: `{}`, but a result was returned: {:?}", @@ -129,6 +136,81 @@ pub(crate) struct Metadata { name: String, } +// TODO(enableNewSmithyRuntimeCleanup): Replace Tee, capture_test_logs, and Rx with +// the implementations added to aws_smithy_runtime::test_util::capture_test_logs +struct Tee { + buf: Arc>>, + quiet: bool, + inner: W, +} + +/// Capture logs from this test. +/// +/// The logs will be captured until the `DefaultGuard` is dropped. +/// +/// *Why use this instead of traced_test?* +/// This captures _all_ logs, not just logs produced by the current crate. +fn capture_test_logs() -> (DefaultGuard, Rx) { + // it may be helpful to upstream this at some point + let (mut writer, rx) = Tee::stdout(); + if env::var("VERBOSE_TEST_LOGS").is_ok() { + writer.loud(); + } else { + eprintln!("To see full logs from this test set VERBOSE_TEST_LOGS=true"); + } + let subscriber = tracing_subscriber::fmt() + .with_max_level(Level::TRACE) + .with_writer(Mutex::new(writer)) + .finish(); + let guard = tracing::subscriber::set_default(subscriber); + (guard, rx) +} + +struct Rx(Arc>>); +impl Rx { + pub(crate) fn contents(&self) -> String { + String::from_utf8(self.0.lock().unwrap().clone()).unwrap() + } +} + +impl Tee { + fn stdout() -> (Self, Rx) { + let buf: Arc>> = Default::default(); + ( + Tee { + buf: buf.clone(), + quiet: true, + inner: TestWriter::new(), + }, + Rx(buf), + ) + } +} + +impl Tee { + fn loud(&mut self) { + self.quiet = false; + } +} + +impl Write for Tee +where + W: Write, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buf.lock().unwrap().extend_from_slice(buf); + if !self.quiet { + self.inner.write(buf) + } else { + Ok(buf.len()) + } + } + + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + impl TestEnvironment { pub(crate) async fn from_dir(dir: impl AsRef) -> Result> { let dir = dir.as_ref(); @@ -232,12 +314,26 @@ impl TestEnvironment { eprintln!("test case: {}. {}", self.metadata.name, self.metadata.docs); } + fn lines_with_secrets<'a>(&'a self, logs: &'a str) -> Vec<&'a str> { + logs.lines().filter(|l| self.contains_secret(l)).collect() + } + + fn contains_secret(&self, log_line: &str) -> bool { + assert!(log_line.lines().count() <= 1); + match &self.metadata.result { + // NOTE: we aren't currently erroring if the session token is leaked, that is in the canonical request among other things + TestResult::Ok(creds) => log_line.contains(&creds.secret_access_key), + TestResult::ErrorContains(_) => false, + } + } + /// Execute a test case. Failures lead to panics. pub(crate) async fn execute(&self, make_provider: impl Fn(ProviderConfig) -> F) where F: Future, P: ProvideCredentials, { + let (_guard, rx) = capture_test_logs(); let provider = make_provider(self.provider_config.clone()).await; let result = provider.provide_credentials().await; tokio::time::pause(); @@ -256,6 +352,14 @@ impl TestEnvironment { Ok(()) => {} Err(e) => panic!("{}", e), } + let contents = rx.contents(); + let leaking_lines = self.lines_with_secrets(&contents); + assert!( + leaking_lines.is_empty(), + "secret was exposed\n{:?}\nSee the following log lines:\n {}", + self.metadata.result, + leaking_lines.join("\n ") + ) } #[track_caller] diff --git a/aws/rust-runtime/aws-config/src/web_identity_token.rs b/aws/rust-runtime/aws-config/src/web_identity_token.rs index dd13524b06..7ea55fdf26 100644 --- a/aws/rust-runtime/aws-config/src/web_identity_token.rs +++ b/aws/rust-runtime/aws-config/src/web_identity_token.rs @@ -64,10 +64,8 @@ use crate::provider_config::ProviderConfig; use crate::sts; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; -use aws_sdk_sts::config::Region; -use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_sdk_sts::operation::assume_role_with_web_identity::AssumeRoleWithWebIdentityInput; -use aws_smithy_client::erase::DynConnector; +use aws_sdk_sts::Client as StsClient; +use aws_smithy_async::time::SharedTimeSource; use aws_smithy_types::error::display::DisplayErrorContext; use aws_types::os_shim_internal::{Env, Fs}; use std::borrow::Cow; @@ -83,9 +81,9 @@ const ENV_VAR_SESSION_NAME: &str = "AWS_ROLE_SESSION_NAME"; #[derive(Debug)] pub struct WebIdentityTokenCredentialsProvider { source: Source, + time_source: SharedTimeSource, fs: Fs, - client: aws_smithy_client::Client, - region: Option, + sts_client: StsClient, } impl WebIdentityTokenCredentialsProvider { @@ -135,9 +133,9 @@ impl WebIdentityTokenCredentialsProvider { "AWS_ROLE_ARN environment variable must be set", ) })?; - let session_name = env - .get(ENV_VAR_SESSION_NAME) - .unwrap_or_else(|_| sts::util::default_session_name("web-identity-token")); + let session_name = env.get(ENV_VAR_SESSION_NAME).unwrap_or_else(|_| { + sts::util::default_session_name("web-identity-token", self.time_source.now()) + }); Ok(Cow::Owned(StaticConfiguration { web_identity_token_file: token_file.into(), role_arn, @@ -151,12 +149,7 @@ impl WebIdentityTokenCredentialsProvider { let conf = self.source()?; load_credentials( &self.fs, - &self.client, - &self.region.as_ref().cloned().ok_or_else(|| { - CredentialsError::invalid_configuration( - "region is required for WebIdentityTokenProvider", - ) - })?, + &self.sts_client, &conf.web_identity_token_file, &conf.role_arn, &conf.session_name, @@ -207,21 +200,19 @@ impl Builder { /// builder, this function will panic. pub fn build(self) -> WebIdentityTokenCredentialsProvider { let conf = self.config.unwrap_or_default(); - let client = conf.sts_client(); let source = self.source.unwrap_or_else(|| Source::Env(conf.env())); WebIdentityTokenCredentialsProvider { source, fs: conf.fs(), - client, - region: conf.region(), + sts_client: StsClient::from_conf(conf.sts_client_config().build()), + time_source: conf.time_source(), } } } async fn load_credentials( fs: &Fs, - client: &aws_smithy_client::Client, - region: &Region, + sts_client: &StsClient, token_file: impl AsRef, role_arn: &str, session_name: &str, @@ -233,23 +224,17 @@ async fn load_credentials( let token = String::from_utf8(token).map_err(|_utf_8_error| { CredentialsError::unhandled("WebIdentityToken was not valid UTF-8") })?; - let conf = aws_sdk_sts::Config::builder() - .region(region.clone()) - .build(); - let operation = AssumeRoleWithWebIdentityInput::builder() + let resp = sts_client.assume_role_with_web_identity() .role_arn(role_arn) .role_session_name(session_name) .web_identity_token(token) - .build() - .expect("valid operation") - .make_operation(&conf) + .send() .await - .expect("valid operation"); - let resp = client.call(operation).await.map_err(|sdk_error| { - tracing::warn!(error = %DisplayErrorContext(&sdk_error), "STS returned an error assuming web identity role"); - CredentialsError::provider_error(sdk_error) - })?; + .map_err(|sdk_error| { + tracing::warn!(error = %DisplayErrorContext(&sdk_error), "STS returned an error assuming web identity role"); + CredentialsError::provider_error(sdk_error) + })?; sts::util::into_credentials(resp.credentials, "WebIdentityToken") } diff --git a/aws/rust-runtime/aws-config/test-data/profile-provider/credential_process/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/profile-provider/credential_process/fs/home/.aws/config index aaec123f3f..6fee217076 100644 --- a/aws/rust-runtime/aws-config/test-data/profile-provider/credential_process/fs/home/.aws/config +++ b/aws/rust-runtime/aws-config/test-data/profile-provider/credential_process/fs/home/.aws/config @@ -1,6 +1,7 @@ [default] source_profile = base -credential_process = echo '{ "Version": 1, "AccessKeyId": "ASIARTESTID", "SecretAccessKey": "TESTSECRETKEY", "SessionToken": "TESTSESSIONTOKEN", "Expiration": "2022-05-02T18:36:00+00:00" }' +# these fake credentials are base64 encoded to prevent triggering the unit test that scans logs for secrets +credential_process = echo eyAiVmVyc2lvbiI6IDEsICJBY2Nlc3NLZXlJZCI6ICJBU0lBUlRFU1RJRCIsICJTZWNyZXRBY2Nlc3NLZXkiOiAiVEVTVFNFQ1JFVEtFWSIsICJTZXNzaW9uVG9rZW4iOiAiVEVTVFNFU1NJT05UT0tFTiIsICJFeHBpcmF0aW9uIjogIjIwMjItMDUtMDJUMTg6MzY6MDArMDA6MDAiIH0K | base64 --decode [profile base] region = us-east-1 diff --git a/aws/rust-runtime/aws-credential-types/Cargo.toml b/aws/rust-runtime/aws-credential-types/Cargo.toml index 8fad933c2a..e109667525 100644 --- a/aws/rust-runtime/aws-credential-types/Cargo.toml +++ b/aws/rust-runtime/aws-credential-types/Cargo.toml @@ -14,20 +14,23 @@ test-util = [] [dependencies] aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } -fastrand = "1.4.0" +fastrand = "2.0.0" tokio = { version = "1.23.1", features = ["sync"] } tracing = "0.1" zeroize = "1" [dev-dependencies] -aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["rt-tokio"] } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["rt-tokio", "test-util"] } # used to test compatibility async-trait = "0.1.51" env_logger = "0.9.0" tokio = { version = "1.23.1", features = ["full", "test-util", "rt"] } -tracing-test = "0.2.1" +tracing-test = "0.2.4" +# TODO(https://github.com/awslabs/smithy-rs/issues/2619): Remove this +# workaround once the fixed is upstreamed. +regex = { version = "1.0", features = ["unicode-case", "unicode-perl"] } [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-credential-types/external-types.toml b/aws/rust-runtime/aws-credential-types/external-types.toml index 83527f561e..e65c743b10 100644 --- a/aws/rust-runtime/aws-credential-types/external-types.toml +++ b/aws/rust-runtime/aws-credential-types/external-types.toml @@ -1,3 +1,6 @@ allowed_external_types = [ - "aws_smithy_async::rt::sleep::AsyncSleep", + "aws_smithy_async::rt::sleep::SharedAsyncSleep", + "aws_smithy_types::config_bag::storable::Storable", + "aws_smithy_types::config_bag::storable::StoreReplace", + "aws_smithy_types::config_bag::storable::Storer", ] diff --git a/aws/rust-runtime/aws-credential-types/src/cache.rs b/aws/rust-runtime/aws-credential-types/src/cache.rs index 23e90c48fa..a1351d0f9c 100644 --- a/aws/rust-runtime/aws-credential-types/src/cache.rs +++ b/aws/rust-runtime/aws-credential-types/src/cache.rs @@ -7,11 +7,14 @@ mod expiring_cache; mod lazy_caching; +mod no_caching; pub use expiring_cache::ExpiringCache; pub use lazy_caching::Builder as LazyBuilder; +use no_caching::NoCredentialsCache; use crate::provider::{future, SharedCredentialsProvider}; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::sync::Arc; /// Asynchronous Cached Credentials Provider @@ -60,9 +63,14 @@ impl ProvideCachedCredentials for SharedCredentialsCache { } } +impl Storable for SharedCredentialsCache { + type Storer = StoreReplace; +} + #[derive(Clone, Debug)] pub(crate) enum Inner { Lazy(lazy_caching::Builder), + NoCaching, } /// `CredentialsCache` allows for configuring and creating a credentials cache. @@ -104,10 +112,22 @@ impl CredentialsCache { lazy_caching::Builder::new() } + /// Creates a [`CredentialsCache`] that offers no caching ability. + pub fn no_caching() -> Self { + Self { + inner: Inner::NoCaching, + } + } + /// Creates a [`SharedCredentialsCache`] wrapping a concrete caching implementation. pub fn create_cache(self, provider: SharedCredentialsProvider) -> SharedCredentialsCache { match self.inner { Inner::Lazy(builder) => SharedCredentialsCache::new(builder.build(provider)), + Inner::NoCaching => SharedCredentialsCache::new(NoCredentialsCache::new(provider)), } } } + +impl Storable for CredentialsCache { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs b/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs index 1081b8f336..6169c2b930 100644 --- a/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs +++ b/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs @@ -5,17 +5,16 @@ //! Lazy, credentials cache implementation -use std::sync::Arc; -use std::time::{Duration, Instant}; +use std::time::Duration; use aws_smithy_async::future::timeout::Timeout; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; +use aws_smithy_async::time::SharedTimeSource; use tracing::{debug, info, info_span, Instrument}; use crate::cache::{ExpiringCache, ProvideCachedCredentials}; use crate::provider::SharedCredentialsProvider; use crate::provider::{error::CredentialsError, future, ProvideCredentials}; -use crate::time_source::TimeSource; const DEFAULT_LOAD_TIMEOUT: Duration = Duration::from_secs(5); const DEFAULT_CREDENTIAL_EXPIRATION: Duration = Duration::from_secs(15 * 60); @@ -24,8 +23,8 @@ const DEFAULT_BUFFER_TIME_JITTER_FRACTION: fn() -> f64 = fastrand::f64; #[derive(Debug)] pub(crate) struct LazyCredentialsCache { - time: TimeSource, - sleeper: Arc, + time: SharedTimeSource, + sleeper: SharedAsyncSleep, cache: ExpiringCache, provider: SharedCredentialsProvider, load_timeout: Duration, @@ -36,8 +35,8 @@ pub(crate) struct LazyCredentialsCache { impl LazyCredentialsCache { fn new( - time: TimeSource, - sleeper: Arc, + time: SharedTimeSource, + sleeper: SharedAsyncSleep, provider: SharedCredentialsProvider, load_timeout: Duration, buffer_time: Duration, @@ -80,7 +79,7 @@ impl ProvideCachedCredentials for LazyCredentialsCache { // since the futures are not eagerly executed, and the cache will only run one // of them. let future = Timeout::new(provider.provide_credentials(), timeout_future); - let start_time = Instant::now(); + let start_time = self.time.now(); let result = cache .get_or_load(|| { let span = info_span!("lazy_load_credentials"); @@ -112,14 +111,14 @@ impl ProvideCachedCredentials for LazyCredentialsCache { // only once for the first thread that succeeds in populating a cache value. info!( "credentials cache miss occurred; added new AWS credentials (took {:?})", - start_time.elapsed() + self.time.now().duration_since(start_time) ); Ok((credentials, expiry + jitter)) } - // Only instrument the the actual load future so that no span - // is opened if the cache decides not to execute it. - .instrument(span) + // Only instrument the the actual load future so that no span + // is opened if the cache decides not to execute it. + .instrument(span) }) .await; debug!("loaded credentials"); @@ -133,14 +132,13 @@ use crate::Credentials; pub use builder::Builder; mod builder { - use std::sync::Arc; use std::time::Duration; use crate::cache::{CredentialsCache, Inner}; use crate::provider::SharedCredentialsProvider; - use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; + use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep}; + use aws_smithy_async::time::SharedTimeSource; - use super::TimeSource; use super::{ LazyCredentialsCache, DEFAULT_BUFFER_TIME, DEFAULT_BUFFER_TIME_JITTER_FRACTION, DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_LOAD_TIMEOUT, @@ -160,8 +158,8 @@ mod builder { /// `build` to create a `LazyCredentialsCache`. #[derive(Clone, Debug, Default)] pub struct Builder { - sleep: Option>, - time_source: Option, + sleep: Option, + time_source: Option, load_timeout: Option, buffer_time: Option, buffer_time_jitter_fraction: Option f64>, @@ -174,34 +172,34 @@ mod builder { Default::default() } - /// Implementation of [`AsyncSleep`] to use for timeouts. + /// Implementation of [`AsyncSleep`](aws_smithy_async::rt::sleep::AsyncSleep) to use for timeouts. /// /// This enables use of the `LazyCredentialsCache` with other async runtimes. /// If using Tokio as the async runtime, this should be set to an instance of /// [`TokioSleep`](aws_smithy_async::rt::sleep::TokioSleep). - pub fn sleep(mut self, sleep: Arc) -> Self { + pub fn sleep(mut self, sleep: SharedAsyncSleep) -> Self { self.set_sleep(Some(sleep)); self } - /// Implementation of [`AsyncSleep`] to use for timeouts. + /// Implementation of [`AsyncSleep`](aws_smithy_async::rt::sleep::AsyncSleep) to use for timeouts. /// /// This enables use of the `LazyCredentialsCache` with other async runtimes. /// If using Tokio as the async runtime, this should be set to an instance of /// [`TokioSleep`](aws_smithy_async::rt::sleep::TokioSleep). - pub fn set_sleep(&mut self, sleep: Option>) -> &mut Self { + pub fn set_sleep(&mut self, sleep: Option) -> &mut Self { self.sleep = sleep; self } #[doc(hidden)] // because they only exist for tests - pub fn time_source(mut self, time_source: TimeSource) -> Self { + pub fn time_source(mut self, time_source: SharedTimeSource) -> Self { self.set_time_source(Some(time_source)); self } #[doc(hidden)] // because they only exist for tests - pub fn set_time_source(&mut self, time_source: Option) -> &mut Self { + pub fn set_time_source(&mut self, time_source: Option) -> &mut Self { self.time_source = time_source; self } @@ -347,38 +345,46 @@ mod tests { use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; - use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; + use aws_smithy_async::test_util::{instant_time_and_sleep, ManualTimeSource}; + use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use tracing::info; use tracing_test::traced_test; use crate::provider::SharedCredentialsProvider; use crate::{ cache::ProvideCachedCredentials, credential_fn::provide_credentials_fn, - provider::error::CredentialsError, time_source::TestingTimeSource, Credentials, + provider::error::CredentialsError, Credentials, }; use super::{ - LazyCredentialsCache, TimeSource, DEFAULT_BUFFER_TIME, DEFAULT_CREDENTIAL_EXPIRATION, + LazyCredentialsCache, DEFAULT_BUFFER_TIME, DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_LOAD_TIMEOUT, }; const BUFFER_TIME_NO_JITTER: fn() -> f64 = || 0_f64; fn test_provider( - time: TimeSource, + time: impl TimeSource + 'static, buffer_time_jitter_fraction: fn() -> f64, load_list: Vec, ) -> LazyCredentialsCache { let load_list = Arc::new(Mutex::new(load_list)); LazyCredentialsCache::new( - time, - Arc::new(TokioSleep::new()), + SharedTimeSource::new(time), + SharedAsyncSleep::new(TokioSleep::new()), SharedCredentialsProvider::new(provide_credentials_fn(move || { let list = load_list.clone(); async move { - let next = list.lock().unwrap().remove(0); - info!("refreshing the credentials to {:?}", next); - next + let mut list = list.lock().unwrap(); + if list.len() > 0 { + let next = list.remove(0); + info!("refreshing the credentials to {:?}", next); + next + } else { + drop(list); + panic!("no more credentials") + } } })), DEFAULT_LOAD_TIMEOUT, @@ -407,14 +413,14 @@ mod tests { #[traced_test] #[tokio::test] async fn initial_populate_credentials() { - let time = TestingTimeSource::new(UNIX_EPOCH); + let time = ManualTimeSource::new(UNIX_EPOCH); let provider = SharedCredentialsProvider::new(provide_credentials_fn(|| async { info!("refreshing the credentials"); Ok(credentials(1000)) })); let credentials_cache = LazyCredentialsCache::new( - TimeSource::testing(&time), - Arc::new(TokioSleep::new()), + SharedTimeSource::new(time), + SharedAsyncSleep::new(TokioSleep::new()), provider, DEFAULT_LOAD_TIMEOUT, DEFAULT_BUFFER_TIME, @@ -435,9 +441,9 @@ mod tests { #[traced_test] #[tokio::test] async fn reload_expired_credentials() { - let mut time = TestingTimeSource::new(epoch_secs(100)); + let time = ManualTimeSource::new(epoch_secs(100)); let credentials_cache = test_provider( - TimeSource::testing(&time), + time.clone(), BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(1000)), @@ -459,9 +465,9 @@ mod tests { #[traced_test] #[tokio::test] async fn load_failed_error() { - let mut time = TestingTimeSource::new(epoch_secs(100)); + let time = ManualTimeSource::new(epoch_secs(100)); let credentials_cache = test_provider( - TimeSource::testing(&time), + time.clone(), BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(1000)), @@ -486,9 +492,9 @@ mod tests { .build() .unwrap(); - let time = TestingTimeSource::new(epoch_secs(0)); + let time = ManualTimeSource::new(epoch_secs(0)); let credentials_cache = Arc::new(test_provider( - TimeSource::testing(&time), + time.clone(), BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(500)), @@ -499,16 +505,15 @@ mod tests { ], )); - let locked_time = Arc::new(Mutex::new(time)); - - for i in 0..4 { + // credentials are available up until 4500 seconds after the unix epoch + // 4*50 = 200 tasks are launched => we can advance time 4500/20 => 225 seconds per advance + for _ in 0..4 { let mut tasks = Vec::new(); - for j in 0..50 { + for _ in 0..50 { let credentials_cache = credentials_cache.clone(); - let time = locked_time.clone(); + let time = time.clone(); tasks.push(rt.spawn(async move { - let now = epoch_secs(i * 1000 + (4 * j)); - time.lock().unwrap().set_time(now); + let now = time.advance(Duration::from_secs(22)); let creds = credentials_cache .provide_cached_credentials() @@ -531,15 +536,15 @@ mod tests { #[tokio::test] #[traced_test] async fn load_timeout() { - let time = TestingTimeSource::new(epoch_secs(100)); + let (time, sleep) = instant_time_and_sleep(epoch_secs(100)); let credentials_cache = LazyCredentialsCache::new( - TimeSource::testing(&time), - Arc::new(TokioSleep::new()), + SharedTimeSource::new(time.clone()), + SharedAsyncSleep::new(sleep), SharedCredentialsProvider::new(provide_credentials_fn(|| async { aws_smithy_async::future::never::Never::new().await; Ok(credentials(1000)) })), - Duration::from_millis(5), + Duration::from_secs(5), DEFAULT_BUFFER_TIME, BUFFER_TIME_NO_JITTER, DEFAULT_CREDENTIAL_EXPIRATION, @@ -549,14 +554,15 @@ mod tests { credentials_cache.provide_cached_credentials().await, Err(CredentialsError::ProviderTimedOut { .. }) )); + assert_eq!(time.now(), epoch_secs(105)); } #[tokio::test] async fn buffer_time_jitter() { - let mut time = TestingTimeSource::new(epoch_secs(100)); + let time = ManualTimeSource::new(epoch_secs(100)); let buffer_time_jitter_fraction = || 0.5_f64; let credentials_cache = test_provider( - TimeSource::testing(&time), + time.clone(), buffer_time_jitter_fraction, vec![Ok(credentials(1000)), Ok(credentials(2000))], ); diff --git a/aws/rust-runtime/aws-credential-types/src/cache/no_caching.rs b/aws/rust-runtime/aws-credential-types/src/cache/no_caching.rs new file mode 100644 index 0000000000..5827f5cea1 --- /dev/null +++ b/aws/rust-runtime/aws-credential-types/src/cache/no_caching.rs @@ -0,0 +1,83 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Credentials cache that offers no caching ability + +use crate::cache::ProvideCachedCredentials; +use crate::provider::SharedCredentialsProvider; +use crate::provider::{future, ProvideCredentials}; +use tracing::debug; + +#[derive(Debug)] +pub(crate) struct NoCredentialsCache { + provider: SharedCredentialsProvider, +} + +impl NoCredentialsCache { + pub(crate) fn new(provider: SharedCredentialsProvider) -> Self { + Self { provider } + } +} + +impl ProvideCachedCredentials for NoCredentialsCache { + fn provide_cached_credentials<'a>(&'a self) -> future::ProvideCredentials<'_> + where + Self: 'a, + { + debug!("Delegating `provide_cached_credentials` to `provide_credentials` on the provider"); + self.provider.provide_credentials() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::credential_fn::provide_credentials_fn; + use crate::Credentials; + use std::sync::{Arc, Mutex}; + use std::time::{Duration, SystemTime}; + + fn test_provider(load_list: Vec) -> NoCredentialsCache { + let load_list = Arc::new(Mutex::new(load_list)); + NoCredentialsCache::new(SharedCredentialsProvider::new(provide_credentials_fn( + move || { + let list = load_list.clone(); + async move { + let next = list.lock().unwrap().remove(0); + next + } + }, + ))) + } + + fn epoch_secs(secs: u64) -> SystemTime { + SystemTime::UNIX_EPOCH + Duration::from_secs(secs) + } + + fn credentials(expired_secs: u64) -> Credentials { + Credentials::new("test", "test", None, Some(epoch_secs(expired_secs)), "test") + } + + async fn expect_creds(expired_secs: u64, provider: &NoCredentialsCache) { + let creds = provider + .provide_cached_credentials() + .await + .expect("expected credentials"); + assert_eq!(Some(epoch_secs(expired_secs)), creds.expiry()); + } + + #[tokio::test] + async fn no_caching() { + let credentials_cache = test_provider(vec![ + Ok(credentials(1000)), + Ok(credentials(2000)), + Ok(credentials(3000)), + ]); + + expect_creds(1000, &credentials_cache).await; + expect_creds(2000, &credentials_cache).await; + expect_creds(3000, &credentials_cache).await; + } +} diff --git a/aws/rust-runtime/aws-credential-types/src/lib.rs b/aws/rust-runtime/aws-credential-types/src/lib.rs index b2f8330b58..de662f6a50 100644 --- a/aws/rust-runtime/aws-credential-types/src/lib.rs +++ b/aws/rust-runtime/aws-credential-types/src/lib.rs @@ -21,7 +21,5 @@ pub mod cache; pub mod credential_fn; mod credentials_impl; pub mod provider; -#[doc(hidden)] -pub mod time_source; pub use credentials_impl::Credentials; diff --git a/aws/rust-runtime/aws-credential-types/src/provider.rs b/aws/rust-runtime/aws-credential-types/src/provider.rs index d9a43ab36d..9be88b590f 100644 --- a/aws/rust-runtime/aws-credential-types/src/provider.rs +++ b/aws/rust-runtime/aws-credential-types/src/provider.rs @@ -72,6 +72,7 @@ construct credentials from hardcoded values. //! ``` use crate::Credentials; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::sync::Arc; /// Credentials provider errors @@ -350,3 +351,7 @@ impl ProvideCredentials for SharedCredentialsProvider { self.0.provide_credentials() } } + +impl Storable for SharedCredentialsProvider { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-credential-types/src/time_source.rs b/aws/rust-runtime/aws-credential-types/src/time_source.rs deleted file mode 100644 index 2639d2f45a..0000000000 --- a/aws/rust-runtime/aws-credential-types/src/time_source.rs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use std::ops::Deref; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, SystemTime}; - -/// Time source abstraction -/// -/// Simple abstraction representing time either real-time or manually-specified for testing -/// -/// # Examples -/// -/// ```rust -/// # struct Client { -/// # // stub -/// # } -/// # -/// # impl Client { -/// # fn with_timesource(ts: TimeSource) -> Self { -/// # Client { } -/// # } -/// # } -/// use aws_credential_types::time_source::TimeSource; -/// let time = TimeSource::default(); -/// let client = Client::with_timesource(time); -/// ``` -#[derive(Debug, Clone)] -pub struct TimeSource(Inner); - -impl TimeSource { - /// Creates `TimeSource` from the manually specified `time_source`. - pub fn testing(time_source: &TestingTimeSource) -> Self { - TimeSource(Inner::Testing(time_source.clone())) - } - - /// Returns the current system time based on the mode. - pub fn now(&self) -> SystemTime { - match &self.0 { - Inner::Default => SystemTime::now(), - Inner::Testing(testing) => testing.now(), - } - } -} - -impl Default for TimeSource { - /// Creates `TimeSource` from the current system time. - fn default() -> Self { - TimeSource(Inner::Default) - } -} - -/// Time Source that can be manually moved for tests -/// -/// # Examples -/// -/// ```rust -/// # struct Client { -/// # // stub -/// # } -/// # -/// # impl Client { -/// # fn with_timesource(ts: TimeSource) -> Self { -/// # Client { } -/// # } -/// # } -/// use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; -/// use std::time::{UNIX_EPOCH, Duration}; -/// let mut time = TestingTimeSource::new(UNIX_EPOCH); -/// let client = Client::with_timesource(TimeSource::testing(&time)); -/// time.advance(Duration::from_secs(100)); -/// ``` -#[derive(Clone, Debug)] -pub struct TestingTimeSource { - queries: Arc>>, - now: Arc>, -} - -impl TestingTimeSource { - /// Creates `TestingTimeSource` with `start_time`. - pub fn new(start_time: SystemTime) -> Self { - Self { - queries: Default::default(), - now: Arc::new(Mutex::new(start_time)), - } - } - - /// Sets time to the specified `time`. - pub fn set_time(&mut self, time: SystemTime) { - let mut now = self.now.lock().unwrap(); - *now = time; - } - - /// Advances time by `delta`. - pub fn advance(&mut self, delta: Duration) { - let mut now = self.now.lock().unwrap(); - *now += delta; - } - - /// Returns a `Vec` of queried times so far. - pub fn queries(&self) -> impl Deref> + '_ { - self.queries.lock().unwrap() - } - - /// Returns the current time understood by `TestingTimeSource`. - pub fn now(&self) -> SystemTime { - let ts = *self.now.lock().unwrap(); - self.queries.lock().unwrap().push(ts); - ts - } -} - -// In the future, if needed we can add a time source trait, however, the testing time source -// should cover most test use cases. -#[derive(Debug, Clone)] -enum Inner { - Default, - Testing(TestingTimeSource), -} - -#[cfg(test)] -mod test { - use super::{TestingTimeSource, TimeSource}; - - use std::time::{Duration, UNIX_EPOCH}; - - #[test] - fn default_time_source_should_not_panic_on_calling_now() { - let time_source = TimeSource::default(); - // no panics - let _ = time_source.now(); - } - - #[test] - fn testing_time_source_should_behave_as_expected() { - let mut testing = TestingTimeSource::new(UNIX_EPOCH); - let time_source = TimeSource::testing(&testing); - assert_eq!(time_source.now(), UNIX_EPOCH); - testing.advance(Duration::from_secs(10)); - assert_eq!(time_source.now(), UNIX_EPOCH + Duration::from_secs(10)); - } -} diff --git a/aws/rust-runtime/aws-http/Cargo.toml b/aws/rust-runtime/aws-http/Cargo.toml index be5cedd5d8..7b01057016 100644 --- a/aws/rust-runtime/aws-http/Cargo.toml +++ b/aws/rust-runtime/aws-http/Cargo.toml @@ -28,10 +28,9 @@ aws-smithy-checksums = { path = "../../../rust-runtime/aws-smithy-checksums" } aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } bytes-utils = "0.1.2" env_logger = "0.9" -http = "0.2.3" tokio = { version = "1.23.1", features = ["macros", "rt", "rt-multi-thread", "test-util", "time"] } -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } -proptest = "1" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +proptest = "1.2" serde = { version = "1", features = ["derive"]} serde_json = "1" diff --git a/aws/rust-runtime/aws-http/external-types.toml b/aws/rust-runtime/aws-http/external-types.toml index 33a2ed14aa..d2e362f515 100644 --- a/aws/rust-runtime/aws-http/external-types.toml +++ b/aws/rust-runtime/aws-http/external-types.toml @@ -5,7 +5,4 @@ allowed_external_types = [ "aws_types::*", "bytes::bytes::Bytes", "http_body::Body", - - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Decide if the following should be exposed - "http::header::value::InvalidHeaderValue", ] diff --git a/aws/rust-runtime/aws-http/src/recursion_detection.rs b/aws/rust-runtime/aws-http/src/recursion_detection.rs index 62faa749cd..3cc27615d3 100644 --- a/aws/rust-runtime/aws-http/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-http/src/recursion_detection.rs @@ -11,6 +11,8 @@ use http::HeaderValue; use percent_encoding::{percent_encode, CONTROLS}; use std::borrow::Cow; +// TODO(enableNewSmithyRuntimeCleanup): Delete this module + /// Recursion Detection Middleware /// /// This middleware inspects the value of the `AWS_LAMBDA_FUNCTION_NAME` and `_X_AMZN_TRACE_ID` environment diff --git a/aws/rust-runtime/aws-http/src/user_agent.rs b/aws/rust-runtime/aws-http/src/user_agent.rs index a9f39dfbf9..27abba7bba 100644 --- a/aws/rust-runtime/aws-http/src/user_agent.rs +++ b/aws/rust-runtime/aws-http/src/user_agent.rs @@ -5,6 +5,7 @@ use aws_smithy_http::middleware::MapRequest; use aws_smithy_http::operation::Request; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use aws_types::app_name::AppName; use aws_types::build_metadata::{OsFamily, BUILD_METADATA}; use aws_types::os_shim_internal::Env; @@ -211,6 +212,10 @@ impl AwsUserAgent { } } +impl Storable for AwsUserAgent { + type Storer = StoreReplace; +} + #[derive(Clone, Copy, Debug)] struct SdkMetadata { name: &'static str, @@ -246,6 +251,10 @@ impl fmt::Display for ApiMetadata { } } +impl Storable for ApiMetadata { + type Storer = StoreReplace; +} + /// Error for when an user agent metadata doesn't meet character requirements. /// /// Metadata may only have alphanumeric characters and any of these characters: @@ -513,6 +522,8 @@ impl fmt::Display for ExecEnvMetadata { } } +// TODO(enableNewSmithyRuntimeCleanup): Delete the user agent Tower middleware and consider moving all the remaining code into aws-runtime + /// User agent middleware #[non_exhaustive] #[derive(Default, Clone, Debug)] @@ -539,6 +550,16 @@ pub struct UserAgentStageError { kind: UserAgentStageErrorKind, } +impl UserAgentStageError { + // `pub(crate)` method instead of implementing `From` so that we + // don't have to expose `InvalidHeaderValue` in public API. + pub(crate) fn from_invalid_header(value: InvalidHeaderValue) -> Self { + Self { + kind: UserAgentStageErrorKind::InvalidHeader(value), + } + } +} + impl Error for UserAgentStageError { fn source(&self) -> Option<&(dyn Error + 'static)> { use UserAgentStageErrorKind::*; @@ -567,17 +588,8 @@ impl From for UserAgentStageError { } } -impl From for UserAgentStageError { - fn from(value: InvalidHeaderValue) -> Self { - Self { - kind: UserAgentStageErrorKind::InvalidHeader(value), - } - } -} - -lazy_static::lazy_static! { - static ref X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); -} +#[allow(clippy::declare_interior_mutable_const)] // we will never mutate this +const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); impl MapRequest for UserAgentStage { type Error = UserAgentStageError; @@ -591,13 +603,16 @@ impl MapRequest for UserAgentStage { let ua = conf .get::() .ok_or(UserAgentStageErrorKind::UserAgentMissing)?; - req.headers_mut() - .append(USER_AGENT, HeaderValue::try_from(ua.ua_header())?); req.headers_mut().append( - X_AMZ_USER_AGENT.clone(), - HeaderValue::try_from(ua.aws_ua_header())?, + USER_AGENT, + HeaderValue::try_from(ua.ua_header()) + .map_err(UserAgentStageError::from_invalid_header)?, + ); + req.headers_mut().append( + X_AMZ_USER_AGENT, + HeaderValue::try_from(ua.aws_ua_header()) + .map_err(UserAgentStageError::from_invalid_header)?, ); - Ok(req) }) } @@ -779,7 +794,7 @@ mod test { .get(USER_AGENT) .expect("UA header should be set"); req.headers() - .get(&*X_AMZ_USER_AGENT) + .get(X_AMZ_USER_AGENT) .expect("UA header should be set"); } } diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index e98507ef63..0b8141b092 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -15,12 +15,17 @@ repository = "https://github.com/awslabs/smithy-rs" aws-credential-types = { path = "../aws-credential-types" } aws-endpoint = { path = "../aws-endpoint" } aws-http = { path = "../aws-http" } +aws-runtime = { path = "../aws-runtime" } +aws-sigv4 = { path = "../aws-sigv4" } aws-sig-auth = { path = "../aws-sig-auth" } aws-smithy-checksums = { path = "../../../rust-runtime/aws-smithy-checksums" } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } -aws-smithy-http-tower= { path = "../../../rust-runtime/aws-smithy-http-tower" } +aws-smithy-http-tower = { path = "../../../rust-runtime/aws-smithy-http-tower" } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["client"] } +aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime", features = ["client"] } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-types = { path = "../aws-types" } bytes = "1" bytes-utils = "0.1.1" @@ -38,8 +43,10 @@ tracing = "0.1" aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client", features = ["test-util"] } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http", features = ["rt-tokio"] } -tempfile = "3.2.0" -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["test-util"] } +tempfile = "3.6.0" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs new file mode 100644 index 0000000000..7dc1a687df --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use http::header::ACCEPT; +use http::HeaderValue; + +/// Interceptor that adds an Accept header to API Gateway requests. +#[derive(Debug, Default)] +pub(crate) struct AcceptHeaderInterceptor; + +impl Interceptor for AcceptHeaderInterceptor { + fn name(&self) -> &'static str { + "AcceptHeaderInterceptor" + } + + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + context + .request_mut() + .headers_mut() + .insert(ACCEPT, HeaderValue::from_static("application/json")); + Ok(()) + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs new file mode 100644 index 0000000000..498844f1b3 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs @@ -0,0 +1,290 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Maintain a cache of discovered endpoints + +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; +use aws_smithy_async::time::SharedTimeSource; +use aws_smithy_client::erase::boxclone::BoxFuture; +use aws_smithy_http::endpoint::{ResolveEndpoint, ResolveEndpointError}; +use aws_smithy_types::endpoint::Endpoint; +use std::fmt::{Debug, Formatter}; +use std::future::Future; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, SystemTime}; +use tokio::sync::oneshot::error::TryRecvError; +use tokio::sync::oneshot::{Receiver, Sender}; + +/// Endpoint reloader +#[must_use] +pub struct ReloadEndpoint { + loader: Box BoxFuture<(Endpoint, SystemTime), ResolveEndpointError> + Send + Sync>, + endpoint: Arc>>, + error: Arc>>, + rx: Receiver<()>, + sleep: SharedAsyncSleep, + time: SharedTimeSource, +} + +impl Debug for ReloadEndpoint { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ReloadEndpoint").finish() + } +} + +impl ReloadEndpoint { + /// Reload the endpoint once + pub async fn reload_once(&self) { + match (self.loader)().await { + Ok((endpoint, expiry)) => { + tracing::debug!("caching resolved endpoint: {:?}", (&endpoint, &expiry)); + *self.endpoint.lock().unwrap() = Some(ExpiringEndpoint { endpoint, expiry }) + } + Err(err) => *self.error.lock().unwrap() = Some(err), + } + } + + /// An infinite loop task that will reload the endpoint + /// + /// This task will terminate when the corresponding [`Client`](crate::Client) is dropped. + pub async fn reload_task(mut self) { + loop { + match self.rx.try_recv() { + Ok(_) | Err(TryRecvError::Closed) => break, + _ => {} + } + self.reload_increment(self.time.now()).await; + self.sleep.sleep(Duration::from_secs(60)).await; + } + } + + async fn reload_increment(&self, now: SystemTime) { + let should_reload = self + .endpoint + .lock() + .unwrap() + .as_ref() + .map(|e| e.is_expired(now)) + .unwrap_or(true); + if should_reload { + tracing::debug!("reloading endpoint, previous endpoint was expired"); + self.reload_once().await; + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct EndpointCache { + error: Arc>>, + endpoint: Arc>>, + // When the sender is dropped, this allows the reload loop to stop + _drop_guard: Arc>, +} + +impl ResolveEndpoint for EndpointCache { + fn resolve_endpoint(&self, _params: &T) -> aws_smithy_http::endpoint::Result { + self.resolve_endpoint() + } +} + +#[derive(Debug)] +struct ExpiringEndpoint { + endpoint: Endpoint, + expiry: SystemTime, +} + +impl ExpiringEndpoint { + fn is_expired(&self, now: SystemTime) -> bool { + tracing::debug!(expiry = ?self.expiry, now = ?now, delta = ?self.expiry.duration_since(now), "checking expiry status of endpoint"); + match self.expiry.duration_since(now) { + Err(_) => true, + Ok(t) => t < Duration::from_secs(120), + } + } +} + +pub(crate) async fn create_cache( + loader_fn: impl Fn() -> F + Send + Sync + 'static, + sleep: SharedAsyncSleep, + time: SharedTimeSource, +) -> Result<(EndpointCache, ReloadEndpoint), ResolveEndpointError> +where + F: Future> + Send + 'static, +{ + let error_holder = Arc::new(Mutex::new(None)); + let endpoint_holder = Arc::new(Mutex::new(None)); + let (tx, rx) = tokio::sync::oneshot::channel(); + let cache = EndpointCache { + error: error_holder.clone(), + endpoint: endpoint_holder.clone(), + _drop_guard: Arc::new(tx), + }; + let reloader = ReloadEndpoint { + loader: Box::new(move || Box::pin((loader_fn)()) as _), + endpoint: endpoint_holder, + error: error_holder, + rx, + sleep, + time, + }; + tracing::debug!("populating initial endpoint discovery cache"); + reloader.reload_once().await; + // if we didn't successfully get an endpoint, bail out so the client knows + // configuration failed to work + cache.resolve_endpoint()?; + Ok((cache, reloader)) +} + +impl EndpointCache { + fn resolve_endpoint(&self) -> aws_smithy_http::endpoint::Result { + tracing::trace!("resolving endpoint from endpoint discovery cache"); + self.endpoint + .lock() + .unwrap() + .as_ref() + .map(|e| e.endpoint.clone()) + .ok_or_else(|| { + self.error + .lock() + .unwrap() + .take() + .unwrap_or_else(|| ResolveEndpointError::message("no endpoint loaded")) + }) + } +} + +#[cfg(test)] +mod test { + use crate::endpoint_discovery::create_cache; + use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; + use aws_smithy_async::test_util::controlled_time_and_sleep; + use aws_smithy_async::time::{SharedTimeSource, SystemTimeSource, TimeSource}; + use aws_smithy_types::endpoint::Endpoint; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + use std::time::{Duration, UNIX_EPOCH}; + use tokio::time::timeout; + + fn check_send_v(t: T) -> T { + t + } + + #[tokio::test] + #[allow(unused_must_use)] + async fn check_traits() { + let (cache, reloader) = create_cache( + || async { + Ok(( + Endpoint::builder().url("http://foo.com").build(), + SystemTimeSource::new().now(), + )) + }, + SharedAsyncSleep::new(TokioSleep::new()), + SharedTimeSource::new(SystemTimeSource::new()), + ) + .await + .unwrap(); + check_send_v(reloader.reload_task()); + check_send_v(cache); + } + + #[tokio::test] + async fn erroring_endpoint_always_reloaded() { + let expiry = UNIX_EPOCH + Duration::from_secs(123456789); + let ct = Arc::new(AtomicUsize::new(0)); + let (cache, reloader) = create_cache( + move || { + let shared_ct = ct.clone(); + shared_ct.fetch_add(1, Ordering::AcqRel); + async move { + Ok(( + Endpoint::builder() + .url(format!("http://foo.com/{shared_ct:?}")) + .build(), + expiry, + )) + } + }, + SharedAsyncSleep::new(TokioSleep::new()), + SharedTimeSource::new(SystemTimeSource::new()), + ) + .await + .expect("returns an endpoint"); + assert_eq!( + cache.resolve_endpoint().expect("ok").url(), + "http://foo.com/1" + ); + // 120 second buffer + reloader + .reload_increment(expiry - Duration::from_secs(240)) + .await; + assert_eq!( + cache.resolve_endpoint().expect("ok").url(), + "http://foo.com/1" + ); + + reloader.reload_increment(expiry).await; + assert_eq!( + cache.resolve_endpoint().expect("ok").url(), + "http://foo.com/2" + ); + } + + #[tokio::test] + async fn test_advance_of_task() { + let expiry = UNIX_EPOCH + Duration::from_secs(123456789); + // expires in 8 minutes + let (time, sleep, mut gate) = controlled_time_and_sleep(expiry - Duration::from_secs(239)); + let ct = Arc::new(AtomicUsize::new(0)); + let (cache, reloader) = create_cache( + move || { + let shared_ct = ct.clone(); + shared_ct.fetch_add(1, Ordering::AcqRel); + async move { + Ok(( + Endpoint::builder() + .url(format!("http://foo.com/{shared_ct:?}")) + .build(), + expiry, + )) + } + }, + SharedAsyncSleep::new(sleep.clone()), + SharedTimeSource::new(time.clone()), + ) + .await + .expect("first load success"); + let reload_task = tokio::spawn(reloader.reload_task()); + assert!(!reload_task.is_finished()); + // expiry occurs after 2 sleeps + // t = 0 + assert_eq!( + gate.expect_sleep().await.duration(), + Duration::from_secs(60) + ); + assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/1"); + // t = 60 + + let sleep = gate.expect_sleep().await; + // we're still holding the drop guard, so we haven't expired yet. + assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/1"); + assert_eq!(sleep.duration(), Duration::from_secs(60)); + sleep.allow_progress(); + // t = 120 + + let sleep = gate.expect_sleep().await; + assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/2"); + sleep.allow_progress(); + + let sleep = gate.expect_sleep().await; + drop(cache); + sleep.allow_progress(); + + timeout(Duration::from_secs(1), reload_task) + .await + .expect("task finishes successfully") + .expect("finishes"); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs b/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs index c60e4d02fe..18f1d9219e 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(enableNewSmithyRuntimeCleanup): Delete this file when cleaning up middleware + use aws_sig_auth::signer::SignableBody; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::{self, ByteStream}; @@ -107,7 +109,7 @@ fn compute_hash_tree(mut hashes: Vec) -> Digest { "even an empty file will produce a digest. this function assumes that hashes is non-empty" ); while hashes.len() > 1 { - let next = hashes.chunks(2).into_iter().map(|chunk| match *chunk { + let next = hashes.chunks(2).map(|chunk| match *chunk { [left, right] => { let mut ctx = Context::new(&SHA256); ctx.update(left.as_ref()); diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs new file mode 100644 index 0000000000..53500eae50 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -0,0 +1,408 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// This code is referenced in generated code, so the compiler doesn't realize it is used. +#![allow(dead_code)] + +use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; +use aws_sigv4::http_request::SignableBody; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::byte_stream; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, +}; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::orchestrator::LoadedRequestBody; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use bytes::Bytes; +use http::header::{HeaderName, HeaderValue}; +use http::Request; +use ring::digest::{Context, Digest, SHA256}; +use std::fmt; +use std::marker::PhantomData; + +/// The default account ID when none is set on an input +const DEFAULT_ACCOUNT_ID: &str = "-"; + +const TREE_HASH_HEADER: &str = "x-amz-sha256-tree-hash"; +const X_AMZ_CONTENT_SHA256: &str = "x-amz-content-sha256"; +const API_VERSION_HEADER: &str = "x-amz-glacier-version"; + +/// Adds an account ID autofill method to generated input structs +/// +/// Some Glacier operations have an account ID field that needs to get defaulted to `-` if not set. +/// This trait is implemented via codegen customization for those operation inputs so that +/// the [`GlacierAccountIdAutofillInterceptor`] can do this defaulting. +pub(crate) trait GlacierAccountId: fmt::Debug { + /// Returns a mutable reference to the account ID field + fn account_id_mut(&mut self) -> &mut Option; + + /// Autofills the account ID with the default if not set + fn autofill_account_id(&mut self) { + let account_id = self.account_id_mut(); + if account_id.as_deref().unwrap_or_default().is_empty() { + *account_id = Some(DEFAULT_ACCOUNT_ID.into()); + } + } +} + +/// Autofills account ID input fields with a default if no value is set +#[derive(Debug)] +pub(crate) struct GlacierAccountIdAutofillInterceptor { + _phantom: PhantomData, +} + +impl GlacierAccountIdAutofillInterceptor { + /// Constructs a new [`GlacierAccountIdAutofillInterceptor`] + pub(crate) fn new() -> Self { + Self { + _phantom: Default::default(), + } + } +} + +impl Interceptor + for GlacierAccountIdAutofillInterceptor +{ + fn name(&self) -> &'static str { + "GlacierAccountIdAutofillInterceptor" + } + + fn modify_before_serialization( + &self, + context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let erased_input = context.input_mut(); + let input: &mut I = erased_input + .downcast_mut() + .expect("typechecked at registration"); + input.autofill_account_id(); + Ok(()) + } +} + +/// Attaches the `x-amz-glacier-version` header to the request +#[derive(Debug)] +pub(crate) struct GlacierApiVersionInterceptor { + api_version: &'static str, +} + +impl GlacierApiVersionInterceptor { + /// Constructs a new [`GlacierApiVersionInterceptor`] with the given API version. + pub(crate) fn new(api_version: &'static str) -> Self { + Self { api_version } + } +} + +impl Interceptor for GlacierApiVersionInterceptor { + fn name(&self) -> &'static str { + "GlacierApiVersionInterceptor" + } + + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + context.request_mut().headers_mut().insert( + API_VERSION_HEADER, + HeaderValue::from_static(self.api_version), + ); + Ok(()) + } +} + +/// Adds a glacier tree hash checksum to the HTTP Request +#[derive(Debug, Default)] +pub(crate) struct GlacierTreeHashHeaderInterceptor; + +impl Interceptor for GlacierTreeHashHeaderInterceptor { + fn name(&self) -> &'static str { + "GlacierTreeHashHeaderInterceptor" + } + + fn modify_before_serialization( + &self, + _context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + // Request the request body to be loaded into memory immediately after serialization + // so that it can be checksummed before signing and transmit + cfg.interceptor_state() + .store_put(LoadedRequestBody::Requested); + Ok(()) + } + + fn modify_before_retry_loop( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let maybe_loaded_body = cfg.load::(); + if let Some(LoadedRequestBody::Loaded(body)) = maybe_loaded_body { + let content_sha256 = add_checksum_treehash(context.request_mut(), body)?; + + // Override the signing payload with this precomputed hash + let mut signing_config = cfg + .load::() + .ok_or("SigV4OperationSigningConfig not found")? + .clone(); + signing_config.signing_options.payload_override = + Some(SignableBody::Precomputed(content_sha256)); + cfg.interceptor_state().store_put(signing_config); + } else { + return Err( + "the request body wasn't loaded into memory before the retry loop, \ + so the Glacier tree hash header can't be computed" + .into(), + ); + } + Ok(()) + } +} + +/// Adds a glacier tree hash checksum to the HTTP Request +/// +/// This handles two cases: +/// 1. A body which is retryable: the body will be streamed through a digest calculator, limiting memory usage. +/// 2. A body which is not retryable: the body will be converted into `Bytes`, then streamed through a digest calculator. +/// +/// The actual checksum algorithm will first compute a SHA256 checksum for each 1MB chunk. Then, a tree +/// will be assembled, recursively pairing neighboring chunks and computing their combined checksum. The 1 leftover +/// chunk (if it exists) is paired at the end. +/// +/// See for more information. +fn add_checksum_treehash( + request: &mut Request, + body: &Bytes, +) -> Result { + let (full_body, hashes) = compute_hashes(body, MEGABYTE)?; + let tree_hash = hex::encode(compute_hash_tree(hashes)); + let complete_hash = hex::encode(full_body); + if !request.headers().contains_key(TREE_HASH_HEADER) { + request.headers_mut().insert( + HeaderName::from_static(TREE_HASH_HEADER), + tree_hash.parse().expect("hash must be valid header"), + ); + } + if !request.headers().contains_key(X_AMZ_CONTENT_SHA256) { + request.headers_mut().insert( + HeaderName::from_static(X_AMZ_CONTENT_SHA256), + complete_hash.parse().expect("hash must be valid header"), + ); + } + Ok(complete_hash) +} + +const MEGABYTE: usize = 1024 * 1024; +fn compute_hashes( + body: &Bytes, + chunk_size: usize, +) -> Result<(Digest, Vec), byte_stream::error::Error> { + let mut hashes = Vec::new(); + let mut full_body = Context::new(&SHA256); + for chunk in body.chunks(chunk_size) { + let mut local = Context::new(&SHA256); + local.update(chunk); + hashes.push(local.finish()); + + full_body.update(chunk); + } + if hashes.is_empty() { + hashes.push(Context::new(&SHA256).finish()) + } + Ok((full_body.finish(), hashes)) +} + +/// Compute the glacier tree hash for a vector of hashes. +/// +/// Adjacent hashes are combined into a single hash. This process occurs recursively until only 1 hash remains. +/// +/// See for more information. +fn compute_hash_tree(mut hashes: Vec) -> Digest { + assert!( + !hashes.is_empty(), + "even an empty file will produce a digest. this function assumes that hashes is non-empty" + ); + while hashes.len() > 1 { + let next = hashes.chunks(2).map(|chunk| match *chunk { + [left, right] => { + let mut ctx = Context::new(&SHA256); + ctx.update(left.as_ref()); + ctx.update(right.as_ref()); + ctx.finish() + } + [last] => last, + _ => unreachable!(), + }); + hashes = next.collect(); + } + hashes[0] +} + +#[cfg(test)] +mod account_id_autofill_tests { + use super::*; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + + #[test] + fn autofill_account_id() { + #[derive(Debug)] + struct SomeInput { + account_id: Option, + } + impl GlacierAccountId for SomeInput { + fn account_id_mut(&mut self) -> &mut Option { + &mut self.account_id + } + } + + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut cfg = ConfigBag::base(); + let mut context = InterceptorContext::new(Input::erase(SomeInput { account_id: None })); + let mut context = BeforeSerializationInterceptorContextMut::from(&mut context); + let interceptor = GlacierAccountIdAutofillInterceptor::::new(); + interceptor + .modify_before_serialization(&mut context, &rc, &mut cfg) + .expect("success"); + assert_eq!( + DEFAULT_ACCOUNT_ID, + context + .input() + .downcast_ref::() + .unwrap() + .account_id + .as_ref() + .expect("it is set now") + ); + } +} + +#[cfg(test)] +mod api_version_tests { + use super::*; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + + #[test] + fn api_version_interceptor() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut cfg = ConfigBag::base(); + let mut context = InterceptorContext::new(Input::doesnt_matter()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let mut context = BeforeTransmitInterceptorContextMut::from(&mut context); + + let interceptor = GlacierApiVersionInterceptor::new("some-version"); + interceptor + .modify_before_signing(&mut context, &rc, &mut cfg) + .expect("success"); + + assert_eq!( + "some-version", + context + .request() + .headers() + .get(API_VERSION_HEADER) + .expect("header set") + ); + } +} + +#[cfg(test)] +mod treehash_checksum_tests { + use super::*; + + #[test] + fn compute_digests() { + { + let body = Bytes::from_static(b"1234"); + let hashes = compute_hashes(&body, 1).expect("succeeds").1; + assert_eq!(hashes.len(), 4); + } + { + let body = Bytes::from_static(b"1234"); + let hashes = compute_hashes(&body, 2).expect("succeeds").1; + assert_eq!(hashes.len(), 2); + } + { + let body = Bytes::from_static(b"12345"); + let hashes = compute_hashes(&body, 3).expect("succeeds").1; + assert_eq!(hashes.len(), 2); + } + { + let body = Bytes::from_static(b"11221122"); + let hashes = compute_hashes(&body, 2).expect("succeeds").1; + assert_eq!(hashes[0].as_ref(), hashes[2].as_ref()); + } + } + + #[test] + fn empty_body_computes_digest() { + let body = Bytes::from_static(b""); + let (_, hashes) = compute_hashes(&body, 2).expect("succeeds"); + assert_eq!(hashes.len(), 1); + } + + #[test] + fn compute_tree_digest() { + macro_rules! hash { + ($($inp:expr),*) => { + { + let mut ctx = ring::digest::Context::new(&ring::digest::SHA256); + $( + ctx.update($inp.as_ref()); + )* + ctx.finish() + } + } + } + let body = Bytes::from_static(b"1234567891011"); + let (complete, hashes) = compute_hashes(&body, 3).expect("succeeds"); + assert_eq!(hashes.len(), 5); + assert_eq!(complete.as_ref(), hash!("1234567891011").as_ref()); + let final_digest = compute_hash_tree(hashes); + let expected_digest = hash!( + hash!( + hash!(hash!("123"), hash!("456")), + hash!(hash!("789"), hash!("101")) + ), + hash!("1") + ); + assert_eq!(expected_digest.as_ref(), final_digest.as_ref()); + } + + #[test] + fn hash_value_test() { + // the test data consists of an 11 byte sequence, repeated. Since the sequence length is + // relatively prime with 1 megabyte, we can ensure that chunks will all have different hashes. + let base_seq = b"01245678912"; + let total_size = MEGABYTE * 101 + 500; + let mut test_data = vec![]; + while test_data.len() < total_size { + test_data.extend_from_slice(base_seq) + } + let test_data = Bytes::from(test_data); + + let mut http_req = http::Request::builder() + .uri("http://example.com/hello") + .body(SdkBody::taken()) // the body isn't used by add_checksum_treehash + .unwrap(); + + add_checksum_treehash(&mut http_req, &test_data).expect("should succeed"); + // hash value verified with AWS CLI + assert_eq!( + http_req.headers().get(TREE_HASH_HEADER).unwrap(), + "3d417484359fc9f5a3bafd576dc47b8b2de2bf2d4fdac5aa2aff768f2210d386" + ); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_body_checksum_middleware.rs similarity index 99% rename from aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs rename to aws/rust-runtime/aws-inlineable/src/http_body_checksum_middleware.rs index 01ecd2f37b..f71e8708e8 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_body_checksum_middleware.rs @@ -248,8 +248,7 @@ fn is_part_level_checksum(checksum: &str) -> bool { #[cfg(test)] mod tests { - use super::wrap_body_with_checksum_validator; - use crate::http_body_checksum::is_part_level_checksum; + use super::{is_part_level_checksum, wrap_body_with_checksum_validator}; use aws_smithy_checksums::ChecksumAlgorithm; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs new file mode 100644 index 0000000000..701a2e36a0 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -0,0 +1,301 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +//! Interceptor for handling Smithy `@httpChecksum` request checksumming with AWS SigV4 + +use aws_http::content_encoding::{AwsChunkedBody, AwsChunkedBodyOptions}; +use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; +use aws_sigv4::http_request::SignableBody; +use aws_smithy_checksums::ChecksumAlgorithm; +use aws_smithy_checksums::{body::calculate, http::HttpChecksum}; +use aws_smithy_http::body::{BoxBody, SdkBody}; +use aws_smithy_http::operation::error::BuildError; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, Input, +}; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; +use http::HeaderValue; +use http_body::Body; +use std::{fmt, mem}; + +/// Errors related to constructing checksum-validated HTTP requests +#[derive(Debug)] +pub(crate) enum Error { + /// Only request bodies with a known size can be checksum validated + UnsizedRequestBody, + ChecksumHeadersAreUnsupportedForStreamingBody, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UnsizedRequestBody => write!( + f, + "Only request bodies with a known size can be checksum validated." + ), + Self::ChecksumHeadersAreUnsupportedForStreamingBody => write!( + f, + "Checksum header insertion is only supported for non-streaming HTTP bodies. \ + To checksum validate a streaming body, the checksums must be sent as trailers." + ), + } + } +} + +impl std::error::Error for Error {} + +#[derive(Debug)] +struct RequestChecksumInterceptorState { + checksum_algorithm: Option, +} +impl Storable for RequestChecksumInterceptorState { + type Storer = StoreReplace; +} + +pub(crate) struct RequestChecksumInterceptor { + algorithm_provider: AP, +} + +impl fmt::Debug for RequestChecksumInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RequestChecksumInterceptor").finish() + } +} + +impl RequestChecksumInterceptor { + pub(crate) fn new(algorithm_provider: AP) -> Self { + Self { algorithm_provider } + } +} + +impl Interceptor for RequestChecksumInterceptor +where + AP: Fn(&Input) -> Result, BoxError> + Send + Sync, +{ + fn name(&self) -> &'static str { + "RequestChecksumInterceptor" + } + + fn read_before_serialization( + &self, + context: &BeforeSerializationInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let checksum_algorithm = (self.algorithm_provider)(context.input())?; + + let mut layer = Layer::new("RequestChecksumInterceptor"); + layer.store_put(RequestChecksumInterceptorState { checksum_algorithm }); + cfg.push_layer(layer); + + Ok(()) + } + + /// Calculate a checksum and modify the request to include the checksum as a header + /// (for in-memory request bodies) or a trailer (for streaming request bodies). + /// Streaming bodies must be sized or this will return an error. + fn modify_before_retry_loop( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let state = cfg + .load::() + .expect("set in `read_before_serialization`"); + + if let Some(checksum_algorithm) = state.checksum_algorithm { + let request = context.request_mut(); + add_checksum_for_request_body(request, checksum_algorithm, cfg)?; + } + + Ok(()) + } +} + +fn add_checksum_for_request_body( + request: &mut http::request::Request, + checksum_algorithm: ChecksumAlgorithm, + cfg: &mut ConfigBag, +) -> Result<(), BoxError> { + match request.body().bytes() { + // Body is in-memory: read it and insert the checksum as a header. + Some(data) => { + tracing::debug!("applying {checksum_algorithm:?} of the request body as a header"); + let mut checksum = checksum_algorithm.into_impl(); + checksum.update(data); + + request + .headers_mut() + .insert(checksum.header_name(), checksum.header_value()); + } + // Body is streaming: wrap the body so it will emit a checksum as a trailer. + None => { + tracing::debug!("applying {checksum_algorithm:?} of the request body as a trailer"); + if let Some(mut signing_config) = cfg.load::().cloned() { + signing_config.signing_options.payload_override = + Some(SignableBody::StreamingUnsignedPayloadTrailer); + cfg.interceptor_state().store_put(signing_config); + } + wrap_streaming_request_body_in_checksum_calculating_body(request, checksum_algorithm)?; + } + } + Ok(()) +} + +fn wrap_streaming_request_body_in_checksum_calculating_body( + request: &mut http::request::Request, + checksum_algorithm: ChecksumAlgorithm, +) -> Result<(), BuildError> { + let original_body_size = request + .body() + .size_hint() + .exact() + .ok_or_else(|| BuildError::other(Error::UnsizedRequestBody))?; + + let mut body = { + let body = mem::replace(request.body_mut(), SdkBody::taken()); + + body.map(move |body| { + let checksum = checksum_algorithm.into_impl(); + let trailer_len = HttpChecksum::size(checksum.as_ref()); + let body = calculate::ChecksumBody::new(body, checksum); + let aws_chunked_body_options = + AwsChunkedBodyOptions::new(original_body_size, vec![trailer_len]); + + let body = AwsChunkedBody::new(body, aws_chunked_body_options); + + SdkBody::from_dyn(BoxBody::new(body)) + }) + }; + + let encoded_content_length = body + .size_hint() + .exact() + .ok_or_else(|| BuildError::other(Error::UnsizedRequestBody))?; + + let headers = request.headers_mut(); + + headers.insert( + http::header::HeaderName::from_static("x-amz-trailer"), + // Convert into a `HeaderName` and then into a `HeaderValue` + http::header::HeaderName::from(checksum_algorithm).into(), + ); + + headers.insert( + http::header::CONTENT_LENGTH, + HeaderValue::from(encoded_content_length), + ); + headers.insert( + http::header::HeaderName::from_static("x-amz-decoded-content-length"), + HeaderValue::from(original_body_size), + ); + headers.insert( + http::header::CONTENT_ENCODING, + HeaderValue::from_str(aws_http::content_encoding::header_value::AWS_CHUNKED) + .map_err(BuildError::other) + .expect("\"aws-chunked\" will always be a valid HeaderValue"), + ); + + mem::swap(request.body_mut(), &mut body); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::http_request_checksum::wrap_streaming_request_body_in_checksum_calculating_body; + use aws_smithy_checksums::ChecksumAlgorithm; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::byte_stream::ByteStream; + use aws_smithy_types::base64; + use bytes::BytesMut; + use http_body::Body; + use tempfile::NamedTempFile; + + #[tokio::test] + async fn test_checksum_body_is_retryable() { + let input_text = "Hello world"; + let chunk_len_hex = format!("{:X}", input_text.len()); + let mut request = http::Request::builder() + .body(SdkBody::retryable(move || SdkBody::from(input_text))) + .unwrap(); + + // ensure original SdkBody is retryable + assert!(request.body().try_clone().is_some()); + + let checksum_algorithm: ChecksumAlgorithm = "crc32".parse().unwrap(); + wrap_streaming_request_body_in_checksum_calculating_body(&mut request, checksum_algorithm) + .unwrap(); + + // ensure wrapped SdkBody is retryable + let mut body = request.body().try_clone().expect("body is retryable"); + + let mut body_data = BytesMut::new(); + while let Some(data) = body.data().await { + body_data.extend_from_slice(&data.unwrap()) + } + let body = std::str::from_utf8(&body_data).unwrap(); + assert_eq!( + format!( + "{chunk_len_hex}\r\n{input_text}\r\n0\r\nx-amz-checksum-crc32:i9aeUg==\r\n\r\n" + ), + body + ); + } + + #[tokio::test] + async fn test_checksum_body_from_file_is_retryable() { + use std::io::Write; + let mut file = NamedTempFile::new().unwrap(); + let checksum_algorithm: ChecksumAlgorithm = "crc32c".parse().unwrap(); + + let mut crc32c_checksum = checksum_algorithm.into_impl(); + for i in 0..10000 { + let line = format!("This is a large file created for testing purposes {}", i); + file.as_file_mut().write_all(line.as_bytes()).unwrap(); + crc32c_checksum.update(line.as_bytes()); + } + let crc32c_checksum = crc32c_checksum.finalize(); + + let mut request = http::Request::builder() + .body( + ByteStream::read_from() + .path(&file) + .buffer_size(1024) + .build() + .await + .unwrap() + .into_inner(), + ) + .unwrap(); + + // ensure original SdkBody is retryable + assert!(request.body().try_clone().is_some()); + + wrap_streaming_request_body_in_checksum_calculating_body(&mut request, checksum_algorithm) + .unwrap(); + + // ensure wrapped SdkBody is retryable + let mut body = request.body().try_clone().expect("body is retryable"); + + let mut body_data = BytesMut::new(); + while let Some(data) = body.data().await { + body_data.extend_from_slice(&data.unwrap()) + } + let body = std::str::from_utf8(&body_data).unwrap(); + let expected_checksum = base64::encode(&crc32c_checksum); + let expected = format!("This is a large file created for testing purposes 9999\r\n0\r\nx-amz-checksum-crc32c:{expected_checksum}\r\n\r\n"); + assert!( + body.ends_with(&expected), + "expected {body} to end with '{expected}'" + ); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs new file mode 100644 index 0000000000..729eb97c4c --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs @@ -0,0 +1,274 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +//! Interceptor for handling Smithy `@httpChecksum` response checksumming + +use aws_smithy_checksums::ChecksumAlgorithm; +use aws_smithy_http::body::{BoxBody, SdkBody}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeDeserializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, Input, +}; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; +use http::HeaderValue; +use std::{fmt, mem}; + +#[derive(Debug)] +struct ResponseChecksumInterceptorState { + validation_enabled: bool, +} +impl Storable for ResponseChecksumInterceptorState { + type Storer = StoreReplace; +} + +pub(crate) struct ResponseChecksumInterceptor { + response_algorithms: &'static [&'static str], + validation_enabled: VE, +} + +impl fmt::Debug for ResponseChecksumInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ResponseChecksumInterceptor") + .field("response_algorithms", &self.response_algorithms) + .finish() + } +} + +impl ResponseChecksumInterceptor { + pub(crate) fn new( + response_algorithms: &'static [&'static str], + validation_enabled: VE, + ) -> Self { + Self { + response_algorithms, + validation_enabled, + } + } +} + +impl Interceptor for ResponseChecksumInterceptor +where + VE: Fn(&Input) -> bool + Send + Sync, +{ + fn name(&self) -> &'static str { + "ResponseChecksumInterceptor" + } + + fn read_before_serialization( + &self, + context: &BeforeSerializationInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let validation_enabled = (self.validation_enabled)(context.input()); + + let mut layer = Layer::new("ResponseChecksumInterceptor"); + layer.store_put(ResponseChecksumInterceptorState { validation_enabled }); + cfg.push_layer(layer); + + Ok(()) + } + + fn modify_before_deserialization( + &self, + context: &mut BeforeDeserializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let state = cfg + .load::() + .expect("set in `read_before_serialization`"); + + if state.validation_enabled { + let response = context.response_mut(); + let maybe_checksum_headers = check_headers_for_precalculated_checksum( + response.headers(), + self.response_algorithms, + ); + if let Some((checksum_algorithm, precalculated_checksum)) = maybe_checksum_headers { + let mut body = SdkBody::taken(); + mem::swap(&mut body, response.body_mut()); + + let mut body = wrap_body_with_checksum_validator( + body, + checksum_algorithm, + precalculated_checksum, + ); + mem::swap(&mut body, response.body_mut()); + } + } + + Ok(()) + } +} + +/// Given an `SdkBody`, a `aws_smithy_checksums::ChecksumAlgorithm`, and a pre-calculated checksum, +/// return an `SdkBody` where the body will processed with the checksum algorithm and checked +/// against the pre-calculated checksum. +pub(crate) fn wrap_body_with_checksum_validator( + body: SdkBody, + checksum_algorithm: ChecksumAlgorithm, + precalculated_checksum: bytes::Bytes, +) -> SdkBody { + use aws_smithy_checksums::body::validate; + + body.map(move |body| { + SdkBody::from_dyn(BoxBody::new(validate::ChecksumBody::new( + body, + checksum_algorithm.into_impl(), + precalculated_checksum.clone(), + ))) + }) +} + +/// Given a `HeaderMap`, extract any checksum included in the headers as `Some(Bytes)`. +/// If no checksum header is set, return `None`. If multiple checksum headers are set, the one that +/// is fastest to compute will be chosen. +pub(crate) fn check_headers_for_precalculated_checksum( + headers: &http::HeaderMap, + response_algorithms: &[&str], +) -> Option<(ChecksumAlgorithm, bytes::Bytes)> { + let checksum_algorithms_to_check = + aws_smithy_checksums::http::CHECKSUM_ALGORITHMS_IN_PRIORITY_ORDER + .into_iter() + // Process list of algorithms, from fastest to slowest, that may have been used to checksum + // the response body, ignoring any that aren't marked as supported algorithms by the model. + .flat_map(|algo| { + // For loop is necessary b/c the compiler doesn't infer the correct lifetimes for iter().find() + for res_algo in response_algorithms { + if algo.eq_ignore_ascii_case(res_algo) { + return Some(algo); + } + } + + None + }); + + for checksum_algorithm in checksum_algorithms_to_check { + let checksum_algorithm: ChecksumAlgorithm = checksum_algorithm.parse().expect( + "CHECKSUM_ALGORITHMS_IN_PRIORITY_ORDER only contains valid checksum algorithm names", + ); + if let Some(precalculated_checksum) = + headers.get(http::HeaderName::from(checksum_algorithm)) + { + let base64_encoded_precalculated_checksum = precalculated_checksum + .to_str() + .expect("base64 uses ASCII characters"); + + // S3 needs special handling for checksums of objects uploaded with `MultiPartUpload`. + if is_part_level_checksum(base64_encoded_precalculated_checksum) { + tracing::warn!( + more_info = "See https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums for more information.", + "This checksum is a part-level checksum which can't be validated by the Rust SDK. Disable checksum validation for this request to fix this warning.", + ); + + return None; + } + + let precalculated_checksum = match aws_smithy_types::base64::decode( + base64_encoded_precalculated_checksum, + ) { + Ok(decoded_checksum) => decoded_checksum.into(), + Err(_) => { + tracing::error!("Checksum received from server could not be base64 decoded. No checksum validation will be performed."); + return None; + } + }; + + return Some((checksum_algorithm, precalculated_checksum)); + } + } + + None +} + +fn is_part_level_checksum(checksum: &str) -> bool { + let mut found_number = false; + let mut found_dash = false; + + for ch in checksum.chars().rev() { + // this could be bad + if ch.is_ascii_digit() { + found_number = true; + continue; + } + + // Yup, it's a part-level checksum + if ch == '-' { + if found_dash { + // Found a second dash?? This isn't a part-level checksum. + return false; + } + + found_dash = true; + continue; + } + + break; + } + + found_number && found_dash +} + +#[cfg(test)] +mod tests { + use super::{is_part_level_checksum, wrap_body_with_checksum_validator}; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::byte_stream::ByteStream; + use aws_smithy_types::error::display::DisplayErrorContext; + use bytes::Bytes; + + #[tokio::test] + async fn test_build_checksum_validated_body_works() { + let checksum_algorithm = "crc32".parse().unwrap(); + let input_text = "Hello world"; + let precalculated_checksum = Bytes::from_static(&[0x8b, 0xd6, 0x9e, 0x52]); + let body = ByteStream::new(SdkBody::from(input_text)); + + let body = body.map(move |sdk_body| { + wrap_body_with_checksum_validator( + sdk_body, + checksum_algorithm, + precalculated_checksum.clone(), + ) + }); + + let mut validated_body = Vec::new(); + if let Err(e) = tokio::io::copy(&mut body.into_async_read(), &mut validated_body).await { + tracing::error!("{}", DisplayErrorContext(&e)); + panic!("checksum validation has failed"); + }; + let body = std::str::from_utf8(&validated_body).unwrap(); + + assert_eq!(input_text, body); + } + + #[test] + fn test_is_multipart_object_checksum() { + // These ARE NOT part-level checksums + assert!(!is_part_level_checksum("abcd")); + assert!(!is_part_level_checksum("abcd=")); + assert!(!is_part_level_checksum("abcd==")); + assert!(!is_part_level_checksum("1234")); + assert!(!is_part_level_checksum("1234=")); + assert!(!is_part_level_checksum("1234==")); + // These ARE part-level checksums + assert!(is_part_level_checksum("abcd-1")); + assert!(is_part_level_checksum("abcd=-12")); + assert!(is_part_level_checksum("abcd12-134")); + assert!(is_part_level_checksum("abcd==-10000")); + // These are gibberish and shouldn't be regarded as a part-level checksum + assert!(!is_part_level_checksum("")); + assert!(!is_part_level_checksum("Spaces? In my header values?")); + assert!(!is_part_level_checksum("abcd==-134!#{!#")); + assert!(!is_part_level_checksum("abcd==-")); + assert!(!is_part_level_checksum("abcd==--11")); + assert!(!is_part_level_checksum("abcd==-AA")); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index d5ed3b8be3..8cc771b9f6 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -19,13 +19,20 @@ unreachable_pub )] +/// Interceptors for API Gateway +pub mod apigateway_interceptors; + /// Stub credentials provider for use when no credentials provider is used. pub mod no_credentials; /// Support types required for adding presigning to an operation in a generated service. pub mod presigning; -// TODO(CrateReorganization): Delete the `old_presigning` module -pub mod old_presigning; + +/// Presigning tower service +pub mod presigning_service; + +/// Presigning interceptors +pub mod presigning_interceptors; /// Special logic for extracting request IDs from S3's responses. pub mod s3_request_id; @@ -33,11 +40,34 @@ pub mod s3_request_id; /// Glacier-specific checksumming behavior pub mod glacier_checksums; +/// Glacier-specific behavior +pub mod glacier_interceptors; + /// Default middleware stack for AWS services pub mod middleware; +/// Strip prefixes from IDs returned by Route53 operations when those IDs are used to construct requests +pub mod route53_resource_id_preprocessor_middleware; + /// Strip prefixes from IDs returned by Route53 operations when those IDs are used to construct requests pub mod route53_resource_id_preprocessor; +pub mod http_request_checksum; +pub mod http_response_checksum; + +// TODO(enableNewSmithyRuntimeCleanup): Delete this module /// Convert a streaming `SdkBody` into an aws-chunked streaming body with checksum trailers -pub mod http_body_checksum; +pub mod http_body_checksum_middleware; + +#[allow(dead_code)] +pub mod endpoint_discovery; + +// This module is symlinked in from the smithy-rs rust-runtime inlineables so that +// the `presigning_interceptors` module can refer to it. +mod serialization_settings; + +// just so docs work +#[allow(dead_code)] +/// allow docs to work +#[derive(Debug)] +pub struct Client; diff --git a/aws/rust-runtime/aws-inlineable/src/old_presigning.rs b/aws/rust-runtime/aws-inlineable/src/old_presigning.rs deleted file mode 100644 index cf95c3901d..0000000000 --- a/aws/rust-runtime/aws-inlineable/src/old_presigning.rs +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Presigned request types and configuration. - -/// Presigning config and builder -pub mod config { - use std::fmt; - use std::time::{Duration, SystemTime}; - - const ONE_WEEK: Duration = Duration::from_secs(604800); - - /// Presigning config values required for creating a presigned request. - #[non_exhaustive] - #[derive(Debug, Clone)] - pub struct PresigningConfig { - start_time: SystemTime, - expires_in: Duration, - } - - impl PresigningConfig { - /// Creates a `PresigningConfig` with the given `expires_in` duration. - /// - /// The `expires_in` duration is the total amount of time the presigned request should - /// be valid for. Other config values are defaulted. - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - pub fn expires_in(expires_in: Duration) -> Result { - Self::builder().expires_in(expires_in).build() - } - - /// Creates a new builder for creating a `PresigningConfig`. - pub fn builder() -> Builder { - Builder::default() - } - - /// Returns the amount of time the presigned request should be valid for. - pub fn expires(&self) -> Duration { - self.expires_in - } - - /// Returns the start time. The presigned request will be valid between this and the end - /// time produced by adding the `expires()` value to it. - pub fn start_time(&self) -> SystemTime { - self.start_time - } - } - - #[derive(Debug)] - enum ErrorKind { - /// Presigned requests cannot be valid for longer than one week. - ExpiresInDurationTooLong, - - /// The `PresigningConfig` builder requires a value for `expires_in`. - ExpiresInRequired, - } - - /// `PresigningConfig` build errors. - #[derive(Debug)] - pub struct Error { - kind: ErrorKind, - } - - impl std::error::Error for Error {} - - impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - ErrorKind::ExpiresInDurationTooLong => { - write!(f, "`expires_in` must be no longer than one week") - } - ErrorKind::ExpiresInRequired => write!(f, "`expires_in` is required"), - } - } - } - - impl From for Error { - fn from(kind: ErrorKind) -> Self { - Self { kind } - } - } - - /// Builder used to create `PresigningConfig`. - #[non_exhaustive] - #[derive(Default, Debug)] - pub struct Builder { - start_time: Option, - expires_in: Option, - } - - impl Builder { - /// Sets the start time for the presigned request. - /// - /// The request will start to be valid at this time, and will cease to be valid after - /// the end time, which can be determined by adding the `expires_in` duration to this - /// start time. If not specified, this will default to the current time. - /// - /// Optional. - pub fn start_time(mut self, start_time: SystemTime) -> Self { - self.set_start_time(Some(start_time)); - self - } - - /// Sets the start time for the presigned request. - /// - /// The request will start to be valid at this time, and will cease to be valid after - /// the end time, which can be determined by adding the `expires_in` duration to this - /// start time. If not specified, this will default to the current time. - /// - /// Optional. - pub fn set_start_time(&mut self, start_time: Option) { - self.start_time = start_time; - } - - /// Sets how long the request should be valid after the `start_time` (which defaults - /// to the current time). - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - /// - /// Required. - pub fn expires_in(mut self, expires_in: Duration) -> Self { - self.set_expires_in(Some(expires_in)); - self - } - - /// Sets how long the request should be valid after the `start_time` (which defaults - /// to the current time). - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - /// - /// Required. - pub fn set_expires_in(&mut self, expires_in: Option) { - self.expires_in = expires_in; - } - - /// Builds the `PresigningConfig`. This will error if `expires_in` is not - /// given, or if it's longer than one week. - pub fn build(self) -> Result { - let expires_in = self.expires_in.ok_or(ErrorKind::ExpiresInRequired)?; - if expires_in > ONE_WEEK { - return Err(ErrorKind::ExpiresInDurationTooLong.into()); - } - Ok(PresigningConfig { - start_time: self.start_time.unwrap_or_else(SystemTime::now), - expires_in, - }) - } - } -} - -/// Presigned request -pub mod request { - use std::fmt::{Debug, Formatter}; - - /// Represents a presigned request. This only includes the HTTP request method, URI, and headers. - /// - /// **This struct has conversion convenience functions:** - /// - /// - [`PresignedRequest::to_http_request`][Self::to_http_request] returns an [`http::Request`](https://docs.rs/http/0.2.6/http/request/struct.Request.html) - /// - [`PresignedRequest::into`](#impl-From) returns an [`http::request::Builder`](https://docs.rs/http/0.2.6/http/request/struct.Builder.html) - #[non_exhaustive] - pub struct PresignedRequest(http::Request<()>); - - impl PresignedRequest { - pub(crate) fn new(inner: http::Request<()>) -> Self { - Self(inner) - } - - /// Returns the HTTP request method. - pub fn method(&self) -> &http::Method { - self.0.method() - } - - /// Returns the HTTP request URI. - pub fn uri(&self) -> &http::Uri { - self.0.uri() - } - - /// Returns any HTTP headers that need to go along with the request, except for `Host`, - /// which should be sent based on the endpoint in the URI by the HTTP client rather than - /// added directly. - pub fn headers(&self) -> &http::HeaderMap { - self.0.headers() - } - - /// Given a body, convert this `PresignedRequest` into an `http::Request` - pub fn to_http_request(self, body: B) -> Result, http::Error> { - let builder: http::request::Builder = self.into(); - - builder.body(body) - } - } - - impl Debug for PresignedRequest { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PresignedRequest") - .field("method", self.method()) - .field("uri", self.uri()) - .field("headers", self.headers()) - .finish() - } - } - - impl From for http::request::Builder { - fn from(req: PresignedRequest) -> Self { - let mut builder = http::request::Builder::new() - .uri(req.uri()) - .method(req.method()); - - if let Some(headers) = builder.headers_mut() { - *headers = req.headers().clone(); - } - - builder - } - } -} - -/// Tower middleware service for creating presigned requests -#[allow(dead_code)] -pub(crate) mod service { - use super::request::PresignedRequest; - use aws_smithy_http::operation; - use http::header::USER_AGENT; - use std::future::{ready, Ready}; - use std::marker::PhantomData; - use std::task::{Context, Poll}; - - /// Tower [`Service`](tower::Service) for generated a [`PresignedRequest`] from the AWS middleware. - #[derive(Default, Debug)] - #[non_exhaustive] - pub(crate) struct PresignedRequestService { - _phantom: PhantomData, - } - - // Required because of the derive Clone on MapRequestService. - // Manually implemented to avoid requiring errors to implement Clone. - impl Clone for PresignedRequestService { - fn clone(&self) -> Self { - Self { - _phantom: Default::default(), - } - } - } - - impl PresignedRequestService { - /// Creates a new `PresignedRequestService` - pub(crate) fn new() -> Self { - Self { - _phantom: Default::default(), - } - } - } - - impl tower::Service for PresignedRequestService { - type Response = PresignedRequest; - type Error = E; - type Future = Ready>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: operation::Request) -> Self::Future { - let (mut req, _) = req.into_parts(); - - // Remove user agent headers since the request will not be executed by the AWS Rust SDK. - req.headers_mut().remove(USER_AGENT); - req.headers_mut().remove("X-Amz-User-Agent"); - - ready(Ok(PresignedRequest::new(req.map(|_| ())))) - } - } -} diff --git a/aws/rust-runtime/aws-inlineable/src/presigning.rs b/aws/rust-runtime/aws-inlineable/src/presigning.rs index da0997d591..c9de75dfb0 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning.rs @@ -147,7 +147,11 @@ impl PresigningConfigBuilder { return Err(ErrorKind::ExpiresInDurationTooLong.into()); } Ok(PresigningConfig { - start_time: self.start_time.unwrap_or_else(SystemTime::now), + start_time: self.start_time.unwrap_or_else( + // This usage is OK—customers can easily override this. + #[allow(clippy::disallowed_methods)] + SystemTime::now, + ), expires_in, }) } @@ -215,60 +219,3 @@ impl From for http::request::Builder { builder } } - -/// Tower middleware service for creating presigned requests -#[allow(dead_code)] -pub(crate) mod service { - use super::PresignedRequest; - use aws_smithy_http::operation; - use http::header::USER_AGENT; - use std::future::{ready, Ready}; - use std::marker::PhantomData; - use std::task::{Context, Poll}; - - /// Tower [`Service`](tower::Service) for generated a [`PresignedRequest`] from the AWS middleware. - #[derive(Default, Debug)] - #[non_exhaustive] - pub(crate) struct PresignedRequestService { - _phantom: PhantomData, - } - - // Required because of the derive Clone on MapRequestService. - // Manually implemented to avoid requiring errors to implement Clone. - impl Clone for PresignedRequestService { - fn clone(&self) -> Self { - Self { - _phantom: Default::default(), - } - } - } - - impl PresignedRequestService { - /// Creates a new `PresignedRequestService` - pub(crate) fn new() -> Self { - Self { - _phantom: Default::default(), - } - } - } - - impl tower::Service for PresignedRequestService { - type Response = PresignedRequest; - type Error = E; - type Future = Ready>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: operation::Request) -> Self::Future { - let (mut req, _) = req.into_parts(); - - // Remove user agent headers since the request will not be executed by the AWS Rust SDK. - req.headers_mut().remove(USER_AGENT); - req.headers_mut().remove("X-Amz-User-Agent"); - - ready(Ok(PresignedRequest::new(req.map(|_| ())))) - } - } -} diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs new file mode 100644 index 0000000000..3ded9fcd68 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -0,0 +1,124 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +use crate::presigning::PresigningConfig; +use crate::serialization_settings::HeaderSerializationSettings; +use aws_runtime::auth::sigv4::{HttpSignatureType, SigV4OperationSigningConfig}; +use aws_runtime::invocation_id::InvocationIdInterceptor; +use aws_runtime::request_info::RequestInfoInterceptor; +use aws_runtime::user_agent::UserAgentInterceptor; +use aws_sigv4::http_request::SignableBody; +use aws_smithy_async::time::{SharedTimeSource, StaticTimeSource}; +use aws_smithy_runtime::client::retries::strategy::NeverRetryStrategy; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, +}; +use aws_smithy_runtime_api::client::interceptors::{ + disable_interceptor, Interceptor, SharedInterceptor, +}; +use aws_smithy_runtime_api::client::retries::SharedRetryStrategy; +use aws_smithy_runtime_api::client::runtime_components::{ + RuntimeComponents, RuntimeComponentsBuilder, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; +use std::borrow::Cow; + +/// Interceptor that tells the SigV4 signer to add the signature to query params, +/// and sets the request expiration time from the presigning config. +#[derive(Debug)] +pub(crate) struct SigV4PresigningInterceptor { + config: PresigningConfig, + payload_override: SignableBody<'static>, +} + +impl SigV4PresigningInterceptor { + pub(crate) fn new(config: PresigningConfig, payload_override: SignableBody<'static>) -> Self { + Self { + config, + payload_override, + } + } +} + +impl Interceptor for SigV4PresigningInterceptor { + fn name(&self) -> &'static str { + "SigV4PresigningInterceptor" + } + + fn modify_before_serialization( + &self, + _context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + cfg.interceptor_state() + .store_put::( + HeaderSerializationSettings::new() + .omit_default_content_length() + .omit_default_content_type(), + ); + Ok(()) + } + + fn modify_before_signing( + &self, + _context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + if let Some(mut config) = cfg.load::().cloned() { + config.signing_options.expires_in = Some(self.config.expires()); + config.signing_options.signature_type = HttpSignatureType::HttpRequestQueryParams; + config.signing_options.payload_override = Some(self.payload_override.clone()); + cfg.interceptor_state() + .store_put::(config); + Ok(()) + } else { + Err( + "SigV4 presigning requires the SigV4OperationSigningConfig to be in the config bag. \ + This is a bug. Please file an issue.".into(), + ) + } + } +} + +/// Runtime plugin that registers the SigV4PresigningInterceptor. +#[derive(Debug)] +pub(crate) struct SigV4PresigningRuntimePlugin { + runtime_components: RuntimeComponentsBuilder, +} + +impl SigV4PresigningRuntimePlugin { + pub(crate) fn new(config: PresigningConfig, payload_override: SignableBody<'static>) -> Self { + let time_source = SharedTimeSource::new(StaticTimeSource::new(config.start_time())); + Self { + runtime_components: RuntimeComponentsBuilder::new("SigV4PresigningRuntimePlugin") + .with_interceptor(SharedInterceptor::new(SigV4PresigningInterceptor::new( + config, + payload_override, + ))) + .with_retry_strategy(Some(SharedRetryStrategy::new(NeverRetryStrategy::new()))) + .with_time_source(Some(time_source)), + } + } +} + +impl RuntimePlugin for SigV4PresigningRuntimePlugin { + fn config(&self) -> Option { + let mut layer = Layer::new("Presigning"); + layer.store_put(disable_interceptor::("presigning")); + layer.store_put(disable_interceptor::("presigning")); + layer.store_put(disable_interceptor::("presigning")); + Some(layer.freeze()) + } + + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.runtime_components) + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_service.rs b/aws/rust-runtime/aws-inlineable/src/presigning_service.rs new file mode 100644 index 0000000000..3dc1e61768 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/presigning_service.rs @@ -0,0 +1,61 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +//! Tower middleware service for creating presigned requests + +use crate::presigning::PresignedRequest; +use aws_smithy_http::operation; +use http::header::USER_AGENT; +use std::future::{ready, Ready}; +use std::marker::PhantomData; +use std::task::{Context, Poll}; + +/// Tower [`Service`](tower::Service) for generated a [`PresignedRequest`] from the AWS middleware. +#[derive(Default, Debug)] +#[non_exhaustive] +pub(crate) struct PresignedRequestService { + _phantom: PhantomData, +} + +// Required because of the derive Clone on MapRequestService. +// Manually implemented to avoid requiring errors to implement Clone. +impl Clone for PresignedRequestService { + fn clone(&self) -> Self { + Self { + _phantom: Default::default(), + } + } +} + +impl PresignedRequestService { + /// Creates a new `PresignedRequestService` + pub(crate) fn new() -> Self { + Self { + _phantom: Default::default(), + } + } +} + +impl tower::Service for PresignedRequestService { + type Response = PresignedRequest; + type Error = E; + type Future = Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: operation::Request) -> Self::Future { + let (mut req, _) = req.into_parts(); + + // Remove user agent headers since the request will not be executed by the AWS Rust SDK. + req.headers_mut().remove(USER_AGENT); + req.headers_mut().remove("X-Amz-User-Agent"); + + ready(Ok(PresignedRequest::new(req.map(|_| ())))) + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs index b1827f065a..32fad6b160 100644 --- a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs +++ b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs @@ -3,10 +3,20 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(dead_code)] + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeSerializationInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use std::fmt; +use std::marker::PhantomData; + // This function is only used to strip prefixes from resource IDs at the time they're passed as // input to a request. Resource IDs returned in responses may or may not include a prefix. /// Strip the resource type prefix from resource ID return -pub fn trim_resource_id(resource_id: &mut Option) { +fn trim_resource_id(resource_id: &mut Option) { const PREFIXES: &[&str] = &[ "/hostedzone/", "hostedzone/", @@ -28,9 +38,60 @@ pub fn trim_resource_id(resource_id: &mut Option) { } } +pub(crate) struct Route53ResourceIdInterceptor +where + G: for<'a> Fn(&'a mut T) -> &'a mut Option, +{ + get_mut_resource_id: G, + _phantom: PhantomData, +} + +impl fmt::Debug for Route53ResourceIdInterceptor +where + G: for<'a> Fn(&'a mut T) -> &'a mut Option, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Route53ResourceIdInterceptor").finish() + } +} + +impl Route53ResourceIdInterceptor +where + G: for<'a> Fn(&'a mut T) -> &'a mut Option, +{ + pub(crate) fn new(get_mut_resource_id: G) -> Self { + Self { + get_mut_resource_id, + _phantom: Default::default(), + } + } +} + +impl Interceptor for Route53ResourceIdInterceptor +where + G: for<'a> Fn(&'a mut T) -> &'a mut Option + Send + Sync, + T: fmt::Debug + Send + Sync + 'static, +{ + fn name(&self) -> &'static str { + "Route53ResourceIdInterceptor" + } + + fn modify_before_serialization( + &self, + context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let input: &mut T = context.input_mut().downcast_mut().expect("correct type"); + let field = (self.get_mut_resource_id)(input); + trim_resource_id(field); + Ok(()) + } +} + #[cfg(test)] mod test { - use crate::route53_resource_id_preprocessor::trim_resource_id; + use super::trim_resource_id; #[test] fn does_not_change_regular_zones() { diff --git a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor_middleware.rs b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor_middleware.rs new file mode 100644 index 0000000000..545be270de --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor_middleware.rs @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// TODO(enableNewSmithyRuntimeCleanup): Delete this module + +// This function is only used to strip prefixes from resource IDs at the time they're passed as +// input to a request. Resource IDs returned in responses may or may not include a prefix. +/// Strip the resource type prefix from resource ID return +pub fn trim_resource_id(resource_id: &mut Option) { + const PREFIXES: &[&str] = &[ + "/hostedzone/", + "hostedzone/", + "/change/", + "change/", + "/delegationset/", + "delegationset/", + ]; + + for prefix in PREFIXES { + if let Some(id) = resource_id + .as_deref() + .unwrap_or_default() + .strip_prefix(prefix) + { + *resource_id = Some(id.to_string()); + return; + } + } +} + +#[cfg(test)] +mod test { + use crate::route53_resource_id_preprocessor_middleware::trim_resource_id; + + #[test] + fn does_not_change_regular_zones() { + struct OperationInput { + resource: Option, + } + + let mut operation = OperationInput { + resource: Some("Z0441723226OZ66S5ZCNZ".to_string()), + }; + trim_resource_id(&mut operation.resource); + assert_eq!( + &operation.resource.unwrap_or_default(), + "Z0441723226OZ66S5ZCNZ" + ); + } + + #[test] + fn sanitizes_prefixed_zone() { + struct OperationInput { + change_id: Option, + } + + let mut operation = OperationInput { + change_id: Some("/change/Z0441723226OZ66S5ZCNZ".to_string()), + }; + trim_resource_id(&mut operation.change_id); + assert_eq!( + &operation.change_id.unwrap_or_default(), + "Z0441723226OZ66S5ZCNZ" + ); + } + + #[test] + fn allow_no_leading_slash() { + struct OperationInput { + hosted_zone: Option, + } + + let mut operation = OperationInput { + hosted_zone: Some("hostedzone/Z0441723226OZ66S5ZCNZ".to_string()), + }; + trim_resource_id(&mut operation.hosted_zone); + assert_eq!( + &operation.hosted_zone.unwrap_or_default(), + "Z0441723226OZ66S5ZCNZ" + ); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/serialization_settings.rs b/aws/rust-runtime/aws-inlineable/src/serialization_settings.rs new file mode 120000 index 0000000000..1df1108b01 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/serialization_settings.rs @@ -0,0 +1 @@ +../../../../rust-runtime/inlineable/src/serialization_settings.rs \ No newline at end of file diff --git a/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs b/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs index 29db9af0fa..3a5c3258d1 100644 --- a/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs +++ b/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs @@ -28,6 +28,7 @@ use aws_http::retry::AwsResponseRetryClassifier; use aws_http::user_agent::AwsUserAgent; use aws_inlineable::middleware::DefaultMiddleware; use aws_sig_auth::signer::OperationSigningConfig; +use aws_smithy_async::time::SharedTimeSource; use aws_types::region::SigningRegion; use aws_types::SigningService; @@ -94,7 +95,9 @@ fn test_operation() -> Operation::Ok(req) }) @@ -104,7 +107,7 @@ fn test_operation() -> Operation"] +description = "Runtime support code for the AWS SDK. This isn't intended to be used directly." +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" + +[dependencies] + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--cfg", "docsrs"] +# End of docs.rs metadata diff --git a/aws/rust-runtime/aws-runtime-api/LICENSE b/aws/rust-runtime/aws-runtime-api/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/aws/rust-runtime/aws-runtime-api/README.md b/aws/rust-runtime/aws-runtime-api/README.md new file mode 100644 index 0000000000..3eb3bd4193 --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/README.md @@ -0,0 +1,8 @@ +aws-runtime-api +=============== + +Runtime support code for the AWS SDK. This crate isn't intended to be used directly. + + +This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. + diff --git a/aws/rust-runtime/aws-runtime-api/external-types.toml b/aws/rust-runtime/aws-runtime-api/external-types.toml new file mode 100644 index 0000000000..ff30ccf5ad --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/external-types.toml @@ -0,0 +1,2 @@ +allowed_external_types = [ +] diff --git a/aws/rust-runtime/aws-runtime-api/src/lib.rs b/aws/rust-runtime/aws-runtime-api/src/lib.rs new file mode 100644 index 0000000000..c66728daf4 --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/src/lib.rs @@ -0,0 +1,14 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Runtime support code for the AWS SDK. This crate isn't intended to be used directly. + +#![warn( + missing_docs, + rustdoc::missing_crate_level_docs, + missing_debug_implementations, + rust_2018_idioms, + unreachable_pub +)] diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml new file mode 100644 index 0000000000..fddff0c9fe --- /dev/null +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "aws-runtime" +version = "0.0.0-smithy-rs-head" +authors = ["AWS Rust SDK Team "] +description = "Runtime support code for the AWS SDK. This crate isn't intended to be used directly." +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" + +[features] +event-stream = ["dep:aws-smithy-eventstream", "aws-sigv4/sign-eventstream"] +test-util = [] + +[dependencies] +aws-credential-types = { path = "../aws-credential-types" } +aws-http = { path = "../aws-http" } +aws-sigv4 = { path = "../aws-sigv4" } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } +aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true } +aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["client"] } +aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } +aws-types = { path = "../aws-types" } +fastrand = "2.0.0" +http = "0.2.3" +percent-encoding = "2.1.0" +tracing = "0.1" +uuid = { version = "1" } + +[dev-dependencies] +aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } +aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types", features = ["test-util"] } +aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["test-util"] } +proptest = "1.2" +serde = { version = "1", features = ["derive"]} +serde_json = "1" +tracing-test = "0.2.4" + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--cfg", "docsrs"] +# End of docs.rs metadata diff --git a/aws/rust-runtime/aws-runtime/LICENSE b/aws/rust-runtime/aws-runtime/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/aws/rust-runtime/aws-runtime/README.md b/aws/rust-runtime/aws-runtime/README.md new file mode 100644 index 0000000000..65b01de9e2 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/README.md @@ -0,0 +1,8 @@ +aws-runtime +=========== + +Runtime support code for the AWS SDK. This crate isn't intended to be used directly. + + +This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. + diff --git a/aws/rust-runtime/aws-runtime/external-types.toml b/aws/rust-runtime/aws-runtime/external-types.toml new file mode 100644 index 0000000000..0449ba09bb --- /dev/null +++ b/aws/rust-runtime/aws-runtime/external-types.toml @@ -0,0 +1,14 @@ +allowed_external_types = [ + "aws_credential_types::*", + "aws_sigv4::*", + "aws_smithy_http::*", + "aws_smithy_types::*", + "aws_smithy_runtime_api::*", + "aws_types::*", + # TODO(audit-external-type-usage) We should newtype these or otherwise avoid exposing them + "http::header::name::HeaderName", + "http::header::value::HeaderValue", + "http::request::Request", + "http::response::Response", + "http::uri::Uri", +] diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections.rs b/aws/rust-runtime/aws-runtime/src/auth.rs similarity index 68% rename from rust-runtime/aws-smithy-runtime/src/client/connections.rs rename to aws/rust-runtime/aws-runtime/src/auth.rs index a46518d15e..149466a58a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections.rs +++ b/aws/rust-runtime/aws-runtime/src/auth.rs @@ -3,5 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(feature = "test-util")] -pub mod test_connection; +/// Auth implementations for SigV4. +pub mod sigv4; diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs new file mode 100644 index 0000000000..ac1eab1ea7 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -0,0 +1,620 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_credential_types::Credentials; +use aws_sigv4::http_request::{ + sign, PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignableBody, + SignableRequest, SignatureLocation, SigningParams, SigningSettings, UriPathNormalizationMode, +}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::auth::{ + AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, Signer, +}; +use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_components::{GetIdentityResolver, RuntimeComponents}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::Document; +use aws_types::region::{Region, SigningRegion}; +use aws_types::SigningService; +use std::borrow::Cow; +use std::error::Error as StdError; +use std::fmt; +use std::time::{Duration, SystemTime}; + +const EXPIRATION_WARNING: &str = "Presigned request will expire before the given \ + `expires_in` duration because the credentials used to sign it will expire first."; + +/// Auth scheme ID for SigV4. +pub const SCHEME_ID: AuthSchemeId = AuthSchemeId::new("sigv4"); + +struct EndpointAuthSchemeConfig { + signing_region_override: Option, + signing_service_override: Option, +} + +#[derive(Debug)] +enum SigV4SigningError { + MissingOperationSigningConfig, + MissingSigningRegion, + MissingSigningService, + WrongIdentityType(Identity), + BadTypeInEndpointAuthSchemeConfig(&'static str), +} + +impl fmt::Display for SigV4SigningError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use SigV4SigningError::*; + let mut w = |s| f.write_str(s); + match self { + MissingOperationSigningConfig => w("missing operation signing config for SigV4"), + MissingSigningRegion => w("missing signing region for SigV4 signing"), + MissingSigningService => w("missing signing service for SigV4 signing"), + WrongIdentityType(identity) => { + write!(f, "wrong identity type for SigV4: {identity:?}") + } + BadTypeInEndpointAuthSchemeConfig(field_name) => { + write!( + f, + "unexpected type for `{field_name}` in endpoint auth scheme config", + ) + } + } + } +} + +impl StdError for SigV4SigningError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Self::MissingOperationSigningConfig => None, + Self::MissingSigningRegion => None, + Self::MissingSigningService => None, + Self::WrongIdentityType(_) => None, + Self::BadTypeInEndpointAuthSchemeConfig(_) => None, + } + } +} + +/// SigV4 auth scheme. +#[derive(Debug, Default)] +pub struct SigV4AuthScheme { + signer: SigV4Signer, +} + +impl SigV4AuthScheme { + /// Creates a new `SigV4AuthScheme`. + pub fn new() -> Self { + Default::default() + } +} + +impl AuthScheme for SigV4AuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn signer(&self) -> &dyn Signer { + &self.signer + } +} + +/// Type of SigV4 signature. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum HttpSignatureType { + /// A signature for a full http request should be computed, with header updates applied to the signing result. + HttpRequestHeaders, + + /// A signature for a full http request should be computed, with query param updates applied to the signing result. + /// + /// This is typically used for presigned URLs. + HttpRequestQueryParams, +} + +/// Signing options for SigV4. +#[derive(Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct SigningOptions { + /// Apply URI encoding twice. + pub double_uri_encode: bool, + /// Apply a SHA-256 payload checksum. + pub content_sha256_header: bool, + /// Normalize the URI path before signing. + pub normalize_uri_path: bool, + /// Omit the session token from the signature. + pub omit_session_token: bool, + /// Optional override for the payload to be used in signing. + pub payload_override: Option>, + /// Signature type. + pub signature_type: HttpSignatureType, + /// Whether or not the signature is optional. + pub signing_optional: bool, + /// Optional expiration (for presigning) + pub expires_in: Option, +} + +impl Default for SigningOptions { + fn default() -> Self { + Self { + double_uri_encode: true, + content_sha256_header: false, + normalize_uri_path: true, + omit_session_token: false, + payload_override: None, + signature_type: HttpSignatureType::HttpRequestHeaders, + signing_optional: false, + expires_in: None, + } + } +} + +/// SigV4 signing configuration for an operation +/// +/// Although these fields MAY be customized on a per request basis, they are generally static +/// for a given operation +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SigV4OperationSigningConfig { + /// AWS Region to sign for. + pub region: Option, + /// AWS Service to sign for. + pub service: Option, + /// Signing options. + pub signing_options: SigningOptions, +} + +impl Storable for SigV4OperationSigningConfig { + type Storer = StoreReplace; +} + +/// SigV4 signer. +#[derive(Debug, Default)] +pub struct SigV4Signer; + +impl SigV4Signer { + /// Creates a new signer instance. + pub fn new() -> Self { + Self + } + + fn settings(operation_config: &SigV4OperationSigningConfig) -> SigningSettings { + let mut settings = SigningSettings::default(); + settings.percent_encoding_mode = if operation_config.signing_options.double_uri_encode { + PercentEncodingMode::Double + } else { + PercentEncodingMode::Single + }; + settings.payload_checksum_kind = if operation_config.signing_options.content_sha256_header { + PayloadChecksumKind::XAmzSha256 + } else { + PayloadChecksumKind::NoHeader + }; + settings.uri_path_normalization_mode = + if operation_config.signing_options.normalize_uri_path { + UriPathNormalizationMode::Enabled + } else { + UriPathNormalizationMode::Disabled + }; + settings.session_token_mode = if operation_config.signing_options.omit_session_token { + SessionTokenMode::Exclude + } else { + SessionTokenMode::Include + }; + settings.signature_location = match operation_config.signing_options.signature_type { + HttpSignatureType::HttpRequestHeaders => SignatureLocation::Headers, + HttpSignatureType::HttpRequestQueryParams => SignatureLocation::QueryParams, + }; + settings.expires_in = operation_config.signing_options.expires_in; + settings + } + + fn signing_params<'a>( + settings: SigningSettings, + credentials: &'a Credentials, + operation_config: &'a SigV4OperationSigningConfig, + request_timestamp: SystemTime, + ) -> Result, SigV4SigningError> { + if let Some(expires_in) = settings.expires_in { + if let Some(creds_expires_time) = credentials.expiry() { + let presigned_expires_time = request_timestamp + expires_in; + if presigned_expires_time > creds_expires_time { + tracing::warn!(EXPIRATION_WARNING); + } + } + } + + let mut builder = SigningParams::builder() + .access_key(credentials.access_key_id()) + .secret_key(credentials.secret_access_key()) + .region( + operation_config + .region + .as_ref() + .ok_or(SigV4SigningError::MissingSigningRegion)? + .as_ref(), + ) + .service_name( + operation_config + .service + .as_ref() + .ok_or(SigV4SigningError::MissingSigningService)? + .as_ref(), + ) + .time(request_timestamp) + .settings(settings); + builder.set_security_token(credentials.session_token()); + Ok(builder.build().expect("all required fields set")) + } + + fn extract_operation_config<'a>( + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'a>, + config_bag: &'a ConfigBag, + ) -> Result, SigV4SigningError> { + let operation_config = config_bag + .load::() + .ok_or(SigV4SigningError::MissingOperationSigningConfig)?; + + let signing_region = config_bag.load::(); + let signing_service = config_bag.load::(); + + let EndpointAuthSchemeConfig { + signing_region_override, + signing_service_override, + } = Self::extract_endpoint_auth_scheme_config(auth_scheme_endpoint_config)?; + + match ( + signing_region_override.or_else(|| signing_region.cloned()), + signing_service_override.or_else(|| signing_service.cloned()), + ) { + (None, None) => Ok(Cow::Borrowed(operation_config)), + (region, service) => { + let mut operation_config = operation_config.clone(); + if region.is_some() { + operation_config.region = region; + } + if service.is_some() { + operation_config.service = service; + } + Ok(Cow::Owned(operation_config)) + } + } + } + + fn extract_endpoint_auth_scheme_config( + endpoint_config: AuthSchemeEndpointConfig<'_>, + ) -> Result { + let (mut signing_region_override, mut signing_service_override) = (None, None); + if let Some(config) = endpoint_config.as_document().and_then(Document::as_object) { + use SigV4SigningError::BadTypeInEndpointAuthSchemeConfig as UnexpectedType; + signing_region_override = match config.get("signingRegion") { + Some(Document::String(s)) => Some(SigningRegion::from(Region::new(s.clone()))), + None => None, + _ => return Err(UnexpectedType("signingRegion")), + }; + signing_service_override = match config.get("signingName") { + Some(Document::String(s)) => Some(SigningService::from(s.to_string())), + None => None, + _ => return Err(UnexpectedType("signingName")), + }; + } + Ok(EndpointAuthSchemeConfig { + signing_region_override, + signing_service_override, + }) + } +} + +impl Signer for SigV4Signer { + fn sign_http_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + runtime_components: &RuntimeComponents, + config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + let operation_config = + Self::extract_operation_config(auth_scheme_endpoint_config, config_bag)?; + let request_time = runtime_components.time_source().unwrap_or_default().now(); + + let credentials = if let Some(creds) = identity.data::() { + creds + } else if operation_config.signing_options.signing_optional { + tracing::debug!("skipped SigV4 signing since signing is optional for this operation and there are no credentials"); + return Ok(()); + } else { + return Err(SigV4SigningError::WrongIdentityType(identity.clone()).into()); + }; + + let settings = Self::settings(&operation_config); + let signing_params = + Self::signing_params(settings, credentials, &operation_config, request_time)?; + + let (signing_instructions, _signature) = { + // A body that is already in memory can be signed directly. A body that is not in memory + // (any sort of streaming body or presigned request) will be signed via UNSIGNED-PAYLOAD. + let signable_body = operation_config + .signing_options + .payload_override + .as_ref() + // the payload_override is a cheap clone because it contains either a + // reference or a short checksum (we're not cloning the entire body) + .cloned() + .unwrap_or_else(|| { + request + .body() + .bytes() + .map(SignableBody::Bytes) + .unwrap_or(SignableBody::UnsignedPayload) + }); + + let signable_request = SignableRequest::new( + request.method(), + request.uri(), + request.headers(), + signable_body, + ); + sign(signable_request, &signing_params)? + } + .into_parts(); + + // If this is an event stream operation, set up the event stream signer + #[cfg(feature = "event-stream")] + { + use aws_smithy_eventstream::frame::DeferredSignerSender; + use event_stream::SigV4MessageSigner; + + if let Some(signer_sender) = config_bag.load::() { + let time_source = runtime_components.time_source().unwrap_or_default(); + signer_sender + .send(Box::new(SigV4MessageSigner::new( + _signature, + credentials.clone(), + Region::new(signing_params.region().to_string()).into(), + signing_params.service_name().to_string().into(), + time_source, + )) as _) + .expect("failed to send deferred signer"); + } + } + + signing_instructions.apply_to_request(request); + Ok(()) + } +} + +#[cfg(feature = "event-stream")] +mod event_stream { + use aws_credential_types::Credentials; + use aws_sigv4::event_stream::{sign_empty_message, sign_message}; + use aws_sigv4::SigningParams; + use aws_smithy_async::time::SharedTimeSource; + use aws_smithy_eventstream::frame::{Message, SignMessage, SignMessageError}; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + + /// Event Stream SigV4 signing implementation. + #[derive(Debug)] + pub(super) struct SigV4MessageSigner { + last_signature: String, + credentials: Credentials, + signing_region: SigningRegion, + signing_service: SigningService, + time: SharedTimeSource, + } + + impl SigV4MessageSigner { + pub(super) fn new( + last_signature: String, + credentials: Credentials, + signing_region: SigningRegion, + signing_service: SigningService, + time: SharedTimeSource, + ) -> Self { + Self { + last_signature, + credentials, + signing_region, + signing_service, + time, + } + } + + fn signing_params(&self) -> SigningParams<'_, ()> { + let mut builder = SigningParams::builder() + .access_key(self.credentials.access_key_id()) + .secret_key(self.credentials.secret_access_key()) + .region(self.signing_region.as_ref()) + .service_name(self.signing_service.as_ref()) + .time(self.time.now()) + .settings(()); + builder.set_security_token(self.credentials.session_token()); + builder.build().unwrap() + } + } + + impl SignMessage for SigV4MessageSigner { + fn sign(&mut self, message: Message) -> Result { + let (signed_message, signature) = { + let params = self.signing_params(); + sign_message(&message, &self.last_signature, ¶ms).into_parts() + }; + self.last_signature = signature; + Ok(signed_message) + } + + fn sign_empty(&mut self) -> Option> { + let (signed_message, signature) = { + let params = self.signing_params(); + sign_empty_message(&self.last_signature, ¶ms).into_parts() + }; + self.last_signature = signature; + Some(Ok(signed_message)) + } + } + + #[cfg(test)] + mod tests { + use super::*; + use aws_credential_types::Credentials; + use aws_smithy_eventstream::frame::{HeaderValue, Message, SignMessage}; + use aws_types::region::Region; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use std::time::{Duration, UNIX_EPOCH}; + + fn check_send_sync(value: T) -> T { + value + } + + #[test] + fn sign_message() { + let region = Region::new("us-east-1"); + let mut signer = check_send_sync(SigV4MessageSigner::new( + "initial-signature".into(), + Credentials::for_tests(), + SigningRegion::from(region), + SigningService::from_static("transcribe"), + SharedTimeSource::new(UNIX_EPOCH + Duration::new(1611160427, 0)), + )); + let mut signatures = Vec::new(); + for _ in 0..5 { + let signed = signer + .sign(Message::new(&b"identical message"[..])) + .unwrap(); + if let HeaderValue::ByteArray(signature) = signed + .headers() + .iter() + .find(|h| h.name().as_str() == ":chunk-signature") + .unwrap() + .value() + { + signatures.push(signature.clone()); + } else { + panic!("failed to get the :chunk-signature") + } + } + for i in 1..signatures.len() { + assert_ne!(signatures[i - 1], signatures[i]); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_credential_types::Credentials; + use aws_sigv4::http_request::SigningSettings; + use aws_smithy_types::config_bag::Layer; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use std::collections::HashMap; + use std::time::{Duration, SystemTime}; + use tracing_test::traced_test; + + #[test] + #[traced_test] + fn expiration_warning() { + let now = SystemTime::UNIX_EPOCH + Duration::from_secs(1000); + let creds_expire_in = Duration::from_secs(100); + + let mut settings = SigningSettings::default(); + settings.expires_in = Some(creds_expire_in - Duration::from_secs(10)); + + let credentials = Credentials::new( + "test-access-key", + "test-secret-key", + Some("test-session-token".into()), + Some(now + creds_expire_in), + "test", + ); + let operation_config = SigV4OperationSigningConfig { + region: Some(SigningRegion::from_static("test")), + service: Some(SigningService::from_static("test")), + signing_options: SigningOptions { + double_uri_encode: true, + content_sha256_header: true, + normalize_uri_path: true, + omit_session_token: true, + signature_type: HttpSignatureType::HttpRequestHeaders, + signing_optional: false, + expires_in: None, + payload_override: None, + }, + }; + SigV4Signer::signing_params(settings, &credentials, &operation_config, now).unwrap(); + assert!(!logs_contain(EXPIRATION_WARNING)); + + let mut settings = SigningSettings::default(); + settings.expires_in = Some(creds_expire_in + Duration::from_secs(10)); + + SigV4Signer::signing_params(settings, &credentials, &operation_config, now).unwrap(); + assert!(logs_contain(EXPIRATION_WARNING)); + } + + #[test] + fn endpoint_config_overrides_region_and_service() { + let mut layer = Layer::new("test"); + layer.store_put(SigV4OperationSigningConfig { + region: Some(SigningRegion::from(Region::new("override-this-region"))), + service: Some(SigningService::from_static("override-this-service")), + signing_options: Default::default(), + }); + let config = Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "sigv4".to_string().into()); + out.insert( + "signingName".to_string(), + "qldb-override".to_string().into(), + ); + out.insert( + "signingRegion".to_string(), + "us-east-override".to_string().into(), + ); + out + }); + let config = AuthSchemeEndpointConfig::from(Some(&config)); + + let cfg = ConfigBag::of_layers(vec![layer]); + let result = SigV4Signer::extract_operation_config(config, &cfg).expect("success"); + + assert_eq!( + result.region, + Some(SigningRegion::from(Region::new("us-east-override"))) + ); + assert_eq!( + result.service, + Some(SigningService::from_static("qldb-override")) + ); + assert!(matches!(result, Cow::Owned(_))); + } + + #[test] + fn endpoint_config_supports_fallback_when_region_or_service_are_unset() { + let mut layer = Layer::new("test"); + layer.store_put(SigV4OperationSigningConfig { + region: Some(SigningRegion::from(Region::new("us-east-1"))), + service: Some(SigningService::from_static("qldb")), + signing_options: Default::default(), + }); + let cfg = ConfigBag::of_layers(vec![layer]); + let config = AuthSchemeEndpointConfig::empty(); + + let result = SigV4Signer::extract_operation_config(config, &cfg).expect("success"); + + assert_eq!( + result.region, + Some(SigningRegion::from(Region::new("us-east-1"))) + ); + assert_eq!(result.service, Some(SigningService::from_static("qldb"))); + assert!(matches!(result, Cow::Borrowed(_))); + } +} diff --git a/aws/rust-runtime/aws-runtime/src/identity.rs b/aws/rust-runtime/aws-runtime/src/identity.rs new file mode 100644 index 0000000000..84e20d2afe --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/identity.rs @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Credentials-based identity support. +pub mod credentials { + use aws_credential_types::cache::SharedCredentialsCache; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; + use aws_smithy_runtime_api::client::orchestrator::Future; + use aws_smithy_types::config_bag::ConfigBag; + + /// Smithy identity resolver for AWS credentials. + #[derive(Debug)] + pub struct CredentialsIdentityResolver { + credentials_cache: SharedCredentialsCache, + } + + impl CredentialsIdentityResolver { + /// Creates a new `CredentialsIdentityResolver`. + pub fn new(credentials_cache: SharedCredentialsCache) -> Self { + Self { credentials_cache } + } + } + + impl IdentityResolver for CredentialsIdentityResolver { + fn resolve_identity(&self, _config_bag: &ConfigBag) -> Future { + let cache = self.credentials_cache.clone(); + Future::new(Box::pin(async move { + let credentials = cache.as_ref().provide_cached_credentials().await?; + let expiration = credentials.expiry(); + Result::<_, BoxError>::Ok(Identity::new(credentials, expiration)) + })) + } + } +} diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs new file mode 100644 index 0000000000..18fcac7c4a --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -0,0 +1,289 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use fastrand::Rng; +use http::{HeaderName, HeaderValue}; +use std::fmt::Debug; +use std::sync::{Arc, Mutex}; + +#[cfg(feature = "test-util")] +pub use test_util::{NoInvocationIdGenerator, PredefinedInvocationIdGenerator}; + +#[allow(clippy::declare_interior_mutable_const)] // we will never mutate this +const AMZ_SDK_INVOCATION_ID: HeaderName = HeaderName::from_static("amz-sdk-invocation-id"); + +/// A generator for returning new invocation IDs on demand. +pub trait InvocationIdGenerator: Debug + Send + Sync { + /// Call this function to receive a new [`InvocationId`] or an error explaining why one couldn't + /// be provided. + fn generate(&self) -> Result, BoxError>; +} + +/// Dynamic dispatch implementation of [`InvocationIdGenerator`] +#[derive(Clone, Debug)] +pub struct SharedInvocationIdGenerator(Arc); + +impl SharedInvocationIdGenerator { + /// Creates a new [`SharedInvocationIdGenerator`]. + pub fn new(gen: impl InvocationIdGenerator + 'static) -> Self { + Self(Arc::new(gen)) + } +} + +impl InvocationIdGenerator for SharedInvocationIdGenerator { + fn generate(&self) -> Result, BoxError> { + self.0.generate() + } +} + +impl Storable for SharedInvocationIdGenerator { + type Storer = StoreReplace; +} + +/// An invocation ID generator that uses random UUIDs for the invocation ID. +#[derive(Debug, Default)] +pub struct DefaultInvocationIdGenerator { + rng: Mutex, +} + +impl DefaultInvocationIdGenerator { + /// Creates a new [`DefaultInvocationIdGenerator`]. + pub fn new() -> Self { + Default::default() + } + + /// Creates a [`DefaultInvocationIdGenerator`] with the given seed. + pub fn with_seed(seed: u64) -> Self { + Self { + rng: Mutex::new(Rng::with_seed(seed)), + } + } +} + +impl InvocationIdGenerator for DefaultInvocationIdGenerator { + fn generate(&self) -> Result, BoxError> { + let mut rng = self.rng.lock().unwrap(); + let mut random_bytes = [0u8; 16]; + rng.fill(&mut random_bytes); + + let id = uuid::Builder::from_random_bytes(random_bytes).into_uuid(); + Ok(Some(InvocationId::new(id.to_string()))) + } +} + +/// This interceptor generates a UUID and attaches it to all request attempts made as part of this operation. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct InvocationIdInterceptor { + default: DefaultInvocationIdGenerator, +} + +impl InvocationIdInterceptor { + /// Creates a new `InvocationIdInterceptor` + pub fn new() -> Self { + Self::default() + } +} + +impl Interceptor for InvocationIdInterceptor { + fn name(&self) -> &'static str { + "InvocationIdInterceptor" + } + + fn modify_before_retry_loop( + &self, + _ctx: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let gen = cfg + .load::() + .map(|gen| gen as &dyn InvocationIdGenerator) + .unwrap_or(&self.default); + if let Some(id) = gen.generate()? { + cfg.interceptor_state().store_put::(id); + } + + Ok(()) + } + + fn modify_before_transmit( + &self, + ctx: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let headers = ctx.request_mut().headers_mut(); + if let Some(id) = cfg.load::() { + headers.append(AMZ_SDK_INVOCATION_ID, id.0.clone()); + } + Ok(()) + } +} + +/// InvocationId provides a consistent ID across retries +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InvocationId(HeaderValue); + +impl InvocationId { + /// Create an invocation ID with the given value. + /// + /// # Panics + /// This constructor will panic if the given invocation ID is not a valid HTTP header value. + pub fn new(invocation_id: String) -> Self { + Self( + HeaderValue::try_from(invocation_id) + .expect("invocation ID must be a valid HTTP header value"), + ) + } +} + +impl Storable for InvocationId { + type Storer = StoreReplace; +} + +#[cfg(feature = "test-util")] +mod test_util { + use super::*; + use std::sync::{Arc, Mutex}; + + impl InvocationId { + /// Create a new invocation ID from a `&'static str`. + pub fn new_from_str(uuid: &'static str) -> Self { + InvocationId(HeaderValue::from_static(uuid)) + } + } + + /// A "generator" that returns [`InvocationId`]s from a predefined list. + #[derive(Debug)] + pub struct PredefinedInvocationIdGenerator { + pre_generated_ids: Arc>>, + } + + impl PredefinedInvocationIdGenerator { + /// Given a `Vec`, create a new [`PredefinedInvocationIdGenerator`]. + pub fn new(mut invocation_ids: Vec) -> Self { + // We're going to pop ids off of the end of the list, so we need to reverse the list or else + // we'll be popping the ids in reverse order, confusing the poor test writer. + invocation_ids.reverse(); + + Self { + pre_generated_ids: Arc::new(Mutex::new(invocation_ids)), + } + } + } + + impl InvocationIdGenerator for PredefinedInvocationIdGenerator { + fn generate(&self) -> Result, BoxError> { + Ok(Some( + self.pre_generated_ids + .lock() + .expect("this will never be under contention") + .pop() + .expect("testers will provide enough invocation IDs"), + )) + } + } + + /// A "generator" that always returns `None`. + #[derive(Debug, Default)] + pub struct NoInvocationIdGenerator; + + impl NoInvocationIdGenerator { + /// Create a new [`NoInvocationIdGenerator`]. + pub fn new() -> Self { + Self::default() + } + } + + impl InvocationIdGenerator for NoInvocationIdGenerator { + fn generate(&self) -> Result, BoxError> { + Ok(None) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeTransmitInterceptorContextMut, Input, InterceptorContext, + }; + use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + use http::HeaderValue; + + fn expect_header<'a>( + context: &'a BeforeTransmitInterceptorContextMut<'_>, + header_name: &str, + ) -> &'a HeaderValue { + context.request().headers().get(header_name).unwrap() + } + + #[test] + fn default_id_generator() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); + + let mut cfg = ConfigBag::base(); + let interceptor = InvocationIdInterceptor::new(); + let mut ctx = Into::into(&mut ctx); + interceptor + .modify_before_retry_loop(&mut ctx, &rc, &mut cfg) + .unwrap(); + interceptor + .modify_before_transmit(&mut ctx, &rc, &mut cfg) + .unwrap(); + + let expected = cfg.load::().expect("invocation ID was set"); + let header = expect_header(&ctx, "amz-sdk-invocation-id"); + assert_eq!(expected.0, header, "the invocation ID in the config bag must match the invocation ID in the request header"); + // UUID should include 32 chars and 4 dashes + assert_eq!(header.len(), 36); + } + + #[cfg(feature = "test-util")] + #[test] + fn custom_id_generator() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); + + let mut cfg = ConfigBag::base(); + let mut layer = Layer::new("test"); + layer.store_put(SharedInvocationIdGenerator::new( + PredefinedInvocationIdGenerator::new(vec![InvocationId::new( + "the-best-invocation-id".into(), + )]), + )); + cfg.push_layer(layer); + + let interceptor = InvocationIdInterceptor::new(); + let mut ctx = Into::into(&mut ctx); + interceptor + .modify_before_retry_loop(&mut ctx, &rc, &mut cfg) + .unwrap(); + interceptor + .modify_before_transmit(&mut ctx, &rc, &mut cfg) + .unwrap(); + + let header = expect_header(&ctx, "amz-sdk-invocation-id"); + assert_eq!("the-best-invocation-id", header); + } +} diff --git a/aws/rust-runtime/aws-runtime/src/lib.rs b/aws/rust-runtime/aws-runtime/src/lib.rs new file mode 100644 index 0000000000..6ad3cec356 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/lib.rs @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Runtime support code for the AWS SDK. This crate isn't intended to be used directly. + +#![warn( + missing_docs, + rustdoc::missing_crate_level_docs, + missing_debug_implementations, + rust_2018_idioms, + unreachable_pub +)] + +/// Supporting code for authentication in the AWS SDK. +pub mod auth; + +/// Supporting code for identity in the AWS SDK. +pub mod identity; + +/// Supporting code for recursion detection in the AWS SDK. +pub mod recursion_detection; + +/// Supporting code for user agent headers in the AWS SDK. +pub mod user_agent; + +/// Supporting code for retry behavior specific to the AWS SDK. +pub mod retries; + +/// Supporting code for invocation ID headers in the AWS SDK. +pub mod invocation_id; + +/// Supporting code for request metadata headers in the AWS SDK. +pub mod request_info; + +/// Interceptor that determines the clock skew between the client and service. +pub mod service_clock_skew; diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs new file mode 100644 index 0000000000..368843a865 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -0,0 +1,182 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use aws_types::os_shim_internal::Env; +use http::HeaderValue; +use percent_encoding::{percent_encode, CONTROLS}; +use std::borrow::Cow; + +const TRACE_ID_HEADER: &str = "x-amzn-trace-id"; + +mod env { + pub(super) const LAMBDA_FUNCTION_NAME: &str = "AWS_LAMBDA_FUNCTION_NAME"; + pub(super) const TRACE_ID: &str = "_X_AMZN_TRACE_ID"; +} + +/// Recursion Detection Interceptor +/// +/// This interceptor inspects the value of the `AWS_LAMBDA_FUNCTION_NAME` and `_X_AMZN_TRACE_ID` environment +/// variables to detect if the request is being invoked in a Lambda function. If it is, the `X-Amzn-Trace-Id` header +/// will be set. This enables downstream services to prevent accidentally infinitely recursive invocations spawned +/// from Lambda. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct RecursionDetectionInterceptor { + env: Env, +} + +impl RecursionDetectionInterceptor { + /// Creates a new `RecursionDetectionInterceptor` + pub fn new() -> Self { + Self::default() + } +} + +impl Interceptor for RecursionDetectionInterceptor { + fn name(&self) -> &'static str { + "RecursionDetectionInterceptor" + } + + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut(); + if request.headers().contains_key(TRACE_ID_HEADER) { + return Ok(()); + } + + if let (Ok(_function_name), Ok(trace_id)) = ( + self.env.get(env::LAMBDA_FUNCTION_NAME), + self.env.get(env::TRACE_ID), + ) { + request + .headers_mut() + .insert(TRACE_ID_HEADER, encode_header(trace_id.as_bytes())); + } + Ok(()) + } +} + +/// Encodes a byte slice as a header. +/// +/// ASCII control characters are percent encoded which ensures that all byte sequences are valid headers +fn encode_header(value: &[u8]) -> HeaderValue { + let value: Cow<'_, str> = percent_encode(value, CONTROLS).into(); + HeaderValue::from_bytes(value.as_bytes()).expect("header is encoded, header must be valid") +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_protocol_test::{assert_ok, validate_headers}; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + use aws_types::os_shim_internal::Env; + use http::HeaderValue; + use proptest::{prelude::*, proptest}; + use serde::Deserialize; + use std::collections::HashMap; + + proptest! { + #[test] + fn header_encoding_never_panics(s in any::>()) { + encode_header(&s); + } + } + + #[test] + fn every_char() { + let buff = (0..=255).collect::>(); + assert_eq!( + encode_header(&buff), + HeaderValue::from_static( + r##"%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"## + ) + ); + } + + #[test] + fn run_tests() { + let test_cases: Vec = + serde_json::from_str(include_str!("../test-data/recursion-detection.json")) + .expect("invalid test case"); + for test_case in test_cases { + check(test_case) + } + } + + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct TestCase { + env: HashMap, + request_headers_before: Vec, + request_headers_after: Vec, + } + + impl TestCase { + fn env(&self) -> Env { + Env::from(self.env.clone()) + } + + /// Headers on the input request + fn request_headers_before(&self) -> impl Iterator { + Self::split_headers(&self.request_headers_before) + } + + /// Headers on the output request + fn request_headers_after(&self) -> impl Iterator { + Self::split_headers(&self.request_headers_after) + } + + /// Split text headers on `: ` + fn split_headers(headers: &[String]) -> impl Iterator { + headers + .iter() + .map(|header| header.split_once(": ").expect("header must contain :")) + } + } + + fn check(test_case: TestCase) { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let env = test_case.env(); + let mut request = http::Request::builder(); + for (name, value) in test_case.request_headers_before() { + request = request.header(name, value); + } + let request = request.body(SdkBody::empty()).expect("must be valid"); + let mut context = InterceptorContext::new(Input::doesnt_matter()); + context.enter_serialization_phase(); + context.set_request(request); + let _ = context.take_input(); + context.enter_before_transmit_phase(); + let mut config = ConfigBag::base(); + + let mut ctx = Into::into(&mut context); + RecursionDetectionInterceptor { env } + .modify_before_signing(&mut ctx, &rc, &mut config) + .expect("interceptor must succeed"); + let mutated_request = context.request().expect("request is set"); + for name in mutated_request.headers().keys() { + assert_eq!( + mutated_request.headers().get_all(name).iter().count(), + 1, + "No duplicated headers" + ) + } + assert_ok(validate_headers( + mutated_request.headers(), + test_case.request_headers_after(), + )) + } +} diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs new file mode 100644 index 0000000000..04825141b3 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -0,0 +1,250 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::service_clock_skew::ServiceClockSkew; +use aws_smithy_async::time::TimeSource; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::retries::RequestAttempts; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::date_time::Format; +use aws_smithy_types::retry::RetryConfig; +use aws_smithy_types::timeout::TimeoutConfig; +use aws_smithy_types::DateTime; +use http::{HeaderName, HeaderValue}; +use std::borrow::Cow; +use std::time::Duration; + +#[allow(clippy::declare_interior_mutable_const)] // we will never mutate this +const AMZ_SDK_REQUEST: HeaderName = HeaderName::from_static("amz-sdk-request"); + +/// Generates and attaches a request header that communicates request-related metadata. +/// Examples include: +/// +/// - When the client will time out this request. +/// - How many times the request has been retried. +/// - The maximum number of retries that the client will attempt. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct RequestInfoInterceptor {} + +impl RequestInfoInterceptor { + /// Creates a new `RequestInfoInterceptor` + pub fn new() -> Self { + RequestInfoInterceptor {} + } +} + +impl RequestInfoInterceptor { + fn build_attempts_pair( + &self, + cfg: &ConfigBag, + ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { + let request_attempts = cfg + .load::() + .map(|r_a| r_a.attempts()) + .unwrap_or(0); + let request_attempts = request_attempts.to_string(); + Some((Cow::Borrowed("attempt"), Cow::Owned(request_attempts))) + } + + fn build_max_attempts_pair( + &self, + cfg: &ConfigBag, + ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { + if let Some(retry_config) = cfg.load::() { + let max_attempts = retry_config.max_attempts().to_string(); + Some((Cow::Borrowed("max"), Cow::Owned(max_attempts))) + } else { + None + } + } + + fn build_ttl_pair( + &self, + cfg: &ConfigBag, + timesource: impl TimeSource, + ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { + let timeout_config = cfg.load::()?; + let socket_read = timeout_config.read_timeout()?; + let estimated_skew: Duration = cfg.load::().cloned()?.into(); + let current_time = timesource.now(); + let ttl = current_time.checked_add(socket_read + estimated_skew)?; + let mut timestamp = DateTime::from(ttl); + // Set subsec_nanos to 0 so that the formatted `DateTime` won't have fractional seconds. + timestamp.set_subsec_nanos(0); + let mut formatted_timestamp = timestamp + .fmt(Format::DateTime) + .expect("the resulting DateTime will always be valid"); + + // Remove dashes and colons + formatted_timestamp = formatted_timestamp + .chars() + .filter(|&c| c != '-' && c != ':') + .collect(); + + Some((Cow::Borrowed("ttl"), Cow::Owned(formatted_timestamp))) + } +} + +impl Interceptor for RequestInfoInterceptor { + fn name(&self) -> &'static str { + "RequestInfoInterceptor" + } + + fn modify_before_transmit( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let mut pairs = RequestPairs::new(); + if let Some(pair) = self.build_ttl_pair( + cfg, + runtime_components + .time_source() + .ok_or("A timesource must be provided")?, + ) { + pairs = pairs.with_pair(pair); + } + if let Some(pair) = self.build_attempts_pair(cfg) { + pairs = pairs.with_pair(pair); + } + if let Some(pair) = self.build_max_attempts_pair(cfg) { + pairs = pairs.with_pair(pair); + } + + let headers = context.request_mut().headers_mut(); + headers.insert(AMZ_SDK_REQUEST, pairs.try_into_header_value()?); + + Ok(()) + } +} + +/// A builder for creating a `RequestPairs` header value. `RequestPairs` is used to generate a +/// retry information header that is sent with every request. The information conveyed by this +/// header allows services to anticipate whether a client will time out or retry a request. +#[derive(Default, Debug)] +pub struct RequestPairs { + inner: Vec<(Cow<'static, str>, Cow<'static, str>)>, +} + +impl RequestPairs { + /// Creates a new `RequestPairs` builder. + pub fn new() -> Self { + Default::default() + } + + /// Adds a pair to the `RequestPairs` builder. + /// Only strings that can be converted to header values are considered valid. + pub fn with_pair( + mut self, + pair: (impl Into>, impl Into>), + ) -> Self { + let pair = (pair.0.into(), pair.1.into()); + self.inner.push(pair); + self + } + + /// Converts the `RequestPairs` builder into a `HeaderValue`. + pub fn try_into_header_value(self) -> Result { + self.try_into() + } +} + +impl TryFrom for HeaderValue { + type Error = BoxError; + + fn try_from(value: RequestPairs) -> Result { + let mut pairs = String::new(); + for (key, value) in value.inner { + if !pairs.is_empty() { + pairs.push_str("; "); + } + + pairs.push_str(&key); + pairs.push('='); + pairs.push_str(&value); + continue; + } + HeaderValue::from_str(&pairs).map_err(Into::into) + } +} + +#[cfg(test)] +mod tests { + use super::RequestInfoInterceptor; + use crate::request_info::RequestPairs; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::context::Input; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + use aws_smithy_types::retry::RetryConfig; + use aws_smithy_types::timeout::TimeoutConfig; + + use http::HeaderValue; + use std::time::Duration; + + fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { + context + .request() + .expect("request is set") + .headers() + .get(header_name) + .unwrap() + .to_str() + .unwrap() + } + + #[test] + fn test_request_pairs_for_initial_attempt() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut context = InterceptorContext::new(Input::doesnt_matter()); + context.enter_serialization_phase(); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let mut layer = Layer::new("test"); + layer.store_put(RetryConfig::standard()); + layer.store_put( + TimeoutConfig::builder() + .read_timeout(Duration::from_secs(30)) + .build(), + ); + let mut config = ConfigBag::of_layers(vec![layer]); + + let _ = context.take_input(); + context.enter_before_transmit_phase(); + let interceptor = RequestInfoInterceptor::new(); + let mut ctx = (&mut context).into(); + interceptor + .modify_before_transmit(&mut ctx, &rc, &mut config) + .unwrap(); + + assert_eq!( + expect_header(&context, "amz-sdk-request"), + "attempt=0; max=3" + ); + } + + #[test] + fn test_header_value_from_request_pairs_supports_all_valid_characters() { + // The list of valid characters is defined by an internal-only spec. + let rp = RequestPairs::new() + .with_pair(("allowed-symbols", "!#$&'*+-.^_`|~")) + .with_pair(("allowed-digits", "01234567890")) + .with_pair(( + "allowed-characters", + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + )) + .with_pair(("allowed-whitespace", " \t")); + let _header_value: HeaderValue = rp + .try_into() + .expect("request pairs can be converted into valid header value."); + } +} diff --git a/aws/rust-runtime/aws-runtime/src/retries.rs b/aws/rust-runtime/aws-runtime/src/retries.rs new file mode 100644 index 0000000000..ed12aa9cde --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/retries.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Classifiers that can inspect a response and determine if it should be retried. +pub mod classifier; diff --git a/aws/rust-runtime/aws-runtime/src/retries/classifier.rs b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs new file mode 100644 index 0000000000..d73cdf4212 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs @@ -0,0 +1,222 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::http::HttpHeaders; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; +use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; +use aws_smithy_types::error::metadata::ProvideErrorMetadata; +use aws_smithy_types::retry::ErrorKind; +use std::error::Error as StdError; +use std::marker::PhantomData; + +/// AWS error codes that represent throttling errors. +pub const THROTTLING_ERRORS: &[&str] = &[ + "Throttling", + "ThrottlingException", + "ThrottledException", + "RequestThrottledException", + "TooManyRequestsException", + "ProvisionedThroughputExceededException", + "TransactionInProgressException", + "RequestLimitExceeded", + "BandwidthLimitExceeded", + "LimitExceededException", + "RequestThrottled", + "SlowDown", + "PriorRequestNotComplete", + "EC2ThrottledException", +]; + +/// AWS error codes that represent transient errors. +pub const TRANSIENT_ERRORS: &[&str] = &["RequestTimeout", "RequestTimeoutException"]; + +/// A retry classifier for determining if the response sent by an AWS service requires a retry. +#[derive(Debug, Default)] +pub struct AwsErrorCodeClassifier { + _inner: PhantomData, +} + +impl AwsErrorCodeClassifier { + /// Create a new AwsErrorCodeClassifier + pub fn new() -> Self { + Self { + _inner: PhantomData, + } + } +} + +impl ClassifyRetry for AwsErrorCodeClassifier +where + E: StdError + ProvideErrorMetadata + Send + Sync + 'static, +{ + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + let error = ctx + .output_or_error()? + .err() + .and_then(OrchestratorError::as_operation_error)? + .downcast_ref::()?; + + if let Some(error_code) = error.code() { + if THROTTLING_ERRORS.contains(&error_code) { + return Some(RetryReason::Error(ErrorKind::ThrottlingError)); + } else if TRANSIENT_ERRORS.contains(&error_code) { + return Some(RetryReason::Error(ErrorKind::TransientError)); + } + }; + + None + } + + fn name(&self) -> &'static str { + "AWS Error Code" + } +} + +/// A retry classifier that checks for `x-amz-retry-after` headers. If one is found, a +/// [`RetryReason::Explicit`] is returned containing the duration to wait before retrying. +#[derive(Debug, Default)] +pub struct AmzRetryAfterHeaderClassifier; + +impl AmzRetryAfterHeaderClassifier { + /// Create a new `AmzRetryAfterHeaderClassifier`. + pub fn new() -> Self { + Self + } +} + +impl ClassifyRetry for AmzRetryAfterHeaderClassifier { + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + ctx.response() + .and_then(|res| res.http_headers().get("x-amz-retry-after")) + .and_then(|header| header.to_str().ok()) + .and_then(|header| header.parse::().ok()) + .map(|retry_after_delay| { + RetryReason::Explicit(std::time::Duration::from_millis(retry_after_delay)) + }) + } + + fn name(&self) -> &'static str { + "'Retry After' Header" + } +} + +#[cfg(test)] +mod test { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::context::{Error, Input}; + use aws_smithy_types::error::ErrorMetadata; + use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; + use std::fmt; + use std::time::Duration; + + #[derive(Debug)] + struct UnmodeledError; + + impl fmt::Display for UnmodeledError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnmodeledError") + } + } + + impl std::error::Error for UnmodeledError {} + + impl ProvideErrorKind for UnmodeledError { + fn retryable_error_kind(&self) -> Option { + None + } + + fn code(&self) -> Option<&str> { + None + } + } + + #[derive(Debug)] + struct CodedError { + metadata: ErrorMetadata, + } + + impl CodedError { + fn new(code: &'static str) -> Self { + Self { + metadata: ErrorMetadata::builder().code(code).build(), + } + } + } + + impl fmt::Display for CodedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Coded Error") + } + } + + impl std::error::Error for CodedError {} + + impl ProvideErrorMetadata for CodedError { + fn meta(&self) -> &ErrorMetadata { + &self.metadata + } + } + + #[test] + fn classify_by_error_code() { + let policy = AwsErrorCodeClassifier::::new(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase( + CodedError::new("Throttling"), + )))); + + assert_eq!( + policy.classify_retry(&ctx), + Some(RetryReason::Error(ErrorKind::ThrottlingError)) + ); + + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase( + CodedError::new("RequestTimeout"), + )))); + assert_eq!( + policy.classify_retry(&ctx), + Some(RetryReason::Error(ErrorKind::TransientError)) + ) + } + + #[test] + fn classify_generic() { + let policy = AwsErrorCodeClassifier::::new(); + let err = aws_smithy_types::Error::builder().code("SlowDown").build(); + let test_response = http::Response::new("OK").map(SdkBody::from); + + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_response(test_response); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase(err)))); + + assert_eq!( + policy.classify_retry(&ctx), + Some(RetryReason::Error(ErrorKind::ThrottlingError)) + ); + } + + #[test] + fn test_retry_after_header() { + let policy = AmzRetryAfterHeaderClassifier; + let res = http::Response::builder() + .header("x-amz-retry-after", "5000") + .body("retry later") + .unwrap() + .map(SdkBody::from); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_response(res); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase( + UnmodeledError, + )))); + + assert_eq!( + policy.classify_retry(&ctx), + Some(RetryReason::Explicit(Duration::from_millis(5000))), + ); + } +} diff --git a/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs b/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs new file mode 100644 index 0000000000..a919f37cad --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs @@ -0,0 +1,98 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeDeserializationInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::date_time::Format; +use aws_smithy_types::DateTime; +use std::time::Duration; + +/// Amount of clock skew between the client and the service. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub(crate) struct ServiceClockSkew { + inner: Duration, +} + +impl ServiceClockSkew { + fn new(inner: Duration) -> Self { + Self { inner } + } +} + +impl Storable for ServiceClockSkew { + type Storer = StoreReplace; +} + +impl From for Duration { + fn from(skew: ServiceClockSkew) -> Duration { + skew.inner + } +} + +/// Interceptor that determines the clock skew between the client and service. +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct ServiceClockSkewInterceptor; + +impl ServiceClockSkewInterceptor { + /// Creates a new `ServiceClockSkewInterceptor`. + pub fn new() -> Self { + Self::default() + } +} + +fn calculate_skew(time_sent: DateTime, time_received: DateTime) -> Duration { + let skew = (time_sent.as_secs_f64() - time_received.as_secs_f64()).max(0.0); + Duration::from_secs_f64(skew) +} + +fn extract_time_sent_from_response( + ctx: &mut BeforeDeserializationInterceptorContextMut<'_>, +) -> Result { + let date_header = ctx + .response() + .headers() + .get("date") + .ok_or("Response from server does not include a `date` header")? + .to_str()?; + DateTime::from_str(date_header, Format::HttpDate).map_err(Into::into) +} + +impl Interceptor for ServiceClockSkewInterceptor { + fn name(&self) -> &'static str { + "ServiceClockSkewInterceptor" + } + + fn modify_before_deserialization( + &self, + ctx: &mut BeforeDeserializationInterceptorContextMut<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let time_received = DateTime::from( + runtime_components + .time_source() + .ok_or("a time source is required (service clock skew)")? + .now(), + ); + let time_sent = match extract_time_sent_from_response(ctx) { + Ok(time_sent) => time_sent, + Err(e) => { + // We don't want to fail a request for this because 1xx and 5xx responses and + // responses from servers with no clock may omit this header. We still log it at the + // trace level to aid in debugging. + tracing::trace!("failed to calculate clock skew of service from response: {e}. Ignoring this error...",); + return Ok(()); + } + }; + let skew = ServiceClockSkew::new(calculate_skew(time_sent, time_received)); + cfg.interceptor_state().store_put(skew); + Ok(()) + } +} diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs new file mode 100644 index 0000000000..7310820f86 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -0,0 +1,253 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use aws_types::app_name::AppName; +use aws_types::os_shim_internal::Env; +use http::header::{InvalidHeaderValue, USER_AGENT}; +use http::{HeaderName, HeaderValue}; +use std::borrow::Cow; +use std::fmt; + +#[allow(clippy::declare_interior_mutable_const)] // we will never mutate this +const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); + +#[derive(Debug)] +enum UserAgentInterceptorError { + MissingApiMetadata, + InvalidHeaderValue(InvalidHeaderValue), +} + +impl std::error::Error for UserAgentInterceptorError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InvalidHeaderValue(source) => Some(source), + Self::MissingApiMetadata => None, + } + } +} + +impl fmt::Display for UserAgentInterceptorError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::InvalidHeaderValue(_) => "AwsUserAgent generated an invalid HTTP header value. This is a bug. Please file an issue.", + Self::MissingApiMetadata => "The UserAgentInterceptor requires ApiMetadata to be set before the request is made. This is a bug. Please file an issue.", + }) + } +} + +impl From for UserAgentInterceptorError { + fn from(err: InvalidHeaderValue) -> Self { + UserAgentInterceptorError::InvalidHeaderValue(err) + } +} + +/// Generates and attaches the AWS SDK's user agent to a HTTP request +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct UserAgentInterceptor; + +impl UserAgentInterceptor { + /// Creates a new `UserAgentInterceptor` + pub fn new() -> Self { + UserAgentInterceptor + } +} + +fn header_values( + ua: &AwsUserAgent, +) -> Result<(HeaderValue, HeaderValue), UserAgentInterceptorError> { + // Pay attention to the extremely subtle difference between ua_header and aws_ua_header below... + Ok(( + HeaderValue::try_from(ua.ua_header())?, + HeaderValue::try_from(ua.aws_ua_header())?, + )) +} + +impl Interceptor for UserAgentInterceptor { + fn name(&self) -> &'static str { + "UserAgentInterceptor" + } + + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let api_metadata = cfg + .load::() + .ok_or(UserAgentInterceptorError::MissingApiMetadata)?; + + // Allow for overriding the user agent by an earlier interceptor (so, for example, + // tests can use `AwsUserAgent::for_tests()`) by attempting to grab one out of the + // config bag before creating one. + let ua: Cow<'_, AwsUserAgent> = cfg + .load::() + .map(Cow::Borrowed) + .unwrap_or_else(|| { + let mut ua = AwsUserAgent::new_from_environment(Env::real(), api_metadata.clone()); + + let maybe_app_name = cfg.load::(); + if let Some(app_name) = maybe_app_name { + ua.set_app_name(app_name.clone()); + } + Cow::Owned(ua) + }); + + let headers = context.request_mut().headers_mut(); + let (user_agent, x_amz_user_agent) = header_values(&ua)?; + headers.append(USER_AGENT, user_agent); + headers.append(X_AMZ_USER_AGENT, x_amz_user_agent); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; + use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + use aws_smithy_types::error::display::DisplayErrorContext; + + fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { + context + .request() + .expect("request is set") + .headers() + .get(header_name) + .unwrap() + .to_str() + .unwrap() + } + + fn context() -> InterceptorContext { + let mut context = InterceptorContext::new(Input::doesnt_matter()); + context.enter_serialization_phase(); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = context.take_input(); + context.enter_before_transmit_phase(); + context + } + + #[test] + fn test_overridden_ua() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut context = context(); + + let mut layer = Layer::new("test"); + layer.store_put(AwsUserAgent::for_tests()); + layer.store_put(ApiMetadata::new("unused", "unused")); + let mut cfg = ConfigBag::of_layers(vec![layer]); + + let interceptor = UserAgentInterceptor::new(); + let mut ctx = Into::into(&mut context); + interceptor + .modify_before_signing(&mut ctx, &rc, &mut cfg) + .unwrap(); + + let header = expect_header(&context, "user-agent"); + assert_eq!(AwsUserAgent::for_tests().ua_header(), header); + assert!(!header.contains("unused")); + + assert_eq!( + AwsUserAgent::for_tests().aws_ua_header(), + expect_header(&context, "x-amz-user-agent") + ); + } + + #[test] + fn test_default_ua() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut context = context(); + + let api_metadata = ApiMetadata::new("some-service", "some-version"); + let mut layer = Layer::new("test"); + layer.store_put(api_metadata.clone()); + let mut config = ConfigBag::of_layers(vec![layer]); + + let interceptor = UserAgentInterceptor::new(); + let mut ctx = Into::into(&mut context); + interceptor + .modify_before_signing(&mut ctx, &rc, &mut config) + .unwrap(); + + let expected_ua = AwsUserAgent::new_from_environment(Env::real(), api_metadata); + assert!( + expected_ua.aws_ua_header().contains("some-service"), + "precondition" + ); + assert_eq!( + expected_ua.ua_header(), + expect_header(&context, "user-agent") + ); + assert_eq!( + expected_ua.aws_ua_header(), + expect_header(&context, "x-amz-user-agent") + ); + } + + #[test] + fn test_app_name() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut context = context(); + + let api_metadata = ApiMetadata::new("some-service", "some-version"); + let mut layer = Layer::new("test"); + layer.store_put(api_metadata); + layer.store_put(AppName::new("my_awesome_app").unwrap()); + let mut config = ConfigBag::of_layers(vec![layer]); + + let interceptor = UserAgentInterceptor::new(); + let mut ctx = Into::into(&mut context); + interceptor + .modify_before_signing(&mut ctx, &rc, &mut config) + .unwrap(); + + let app_value = "app/my_awesome_app"; + let header = expect_header(&context, "user-agent"); + assert!( + !header.contains(app_value), + "expected `{header}` to not contain `{app_value}`" + ); + + let header = expect_header(&context, "x-amz-user-agent"); + assert!( + header.contains(app_value), + "expected `{header}` to contain `{app_value}`" + ); + } + + #[test] + fn test_api_metadata_missing() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut context = context(); + let mut config = ConfigBag::base(); + + let interceptor = UserAgentInterceptor::new(); + let mut ctx = Into::into(&mut context); + + let error = format!( + "{}", + DisplayErrorContext( + &*interceptor + .modify_before_signing(&mut ctx, &rc, &mut config) + .expect_err("it should error") + ) + ); + assert!( + error.contains("This is a bug"), + "`{error}` should contain message `This is a bug`" + ); + } +} diff --git a/aws/rust-runtime/aws-runtime/test-data/recursion-detection.json b/aws/rust-runtime/aws-runtime/test-data/recursion-detection.json new file mode 100644 index 0000000000..59a44307f7 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/test-data/recursion-detection.json @@ -0,0 +1,71 @@ +[ + { + "env": {}, + "requestHeadersBefore": [], + "requestHeadersAfter": [], + "description": [ + "The AWS_LAMBDA_FUNCTION_NAME and _X_AMZN_TRACE_ID environment variables are not set.", + "There should be no X-Amzn-Trace-Id header sent." + ] + }, + { + "env": { + "AWS_LAMBDA_FUNCTION_NAME": "some-function", + "_X_AMZN_TRACE_ID": "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + }, + "requestHeadersBefore": [], + "requestHeadersAfter": [ + "X-Amzn-Trace-Id: Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + ], + "description": [ + "AWS_LAMBDA_FUNCTION_NAME is set, and", + "_X_AMZN_TRACE_ID is set to \"Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2\".", + "The X-Amzn-Trace-Id header should be sent with that value." + ] + }, + { + "env": { + "_X_AMZN_TRACE_ID": "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + }, + "requestHeadersBefore": [], + "requestHeadersAfter": [], + "description": [ + "AWS_LAMBDA_FUNCTION_NAME is NOT set, and", + "_X_AMZN_TRACE_ID is set to \"Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2\".", + "The X-Amzn-Trace-Id header should NOT be sent with that value." + ] + }, + { + "env": { + "AWS_LAMBDA_FUNCTION_NAME": "some-function", + "_X_AMZN_TRACE_ID": "EnvValue" + }, + "requestHeadersBefore": [ + "X-Amzn-Trace-Id: OriginalValue" + ], + "requestHeadersAfter": [ + "X-Amzn-Trace-Id: OriginalValue" + ], + "desciption": [ + "AWS_LAMBDA_FUNCTION_NAME is set, and", + "_X_AMZN_TRACE_ID is set to \"EnvValue\",", + "but the X-Amzn-Trace-Id header is already set on the request.", + "The X-Amzn-Trace-Id header should keep its original value." + ] + }, + { + "env": { + "AWS_LAMBDA_FUNCTION_NAME": "some-function", + "_X_AMZN_TRACE_ID": "first\nsecond¼\t" + }, + "requestHeadersBefore": [], + "requestHeadersAfter": [ + "X-Amzn-Trace-Id: first%0Asecond%C2%BC%09" + ], + "description": [ + "AWS_LAMBDA_FUNCTION_NAME is set, and", + "_X_AMZN_TRACE_ID has ASCII control characters in it.", + "The X-Amzn-Trace-Id header is added with the control characters percent encoded." + ] + } +] diff --git a/aws/rust-runtime/aws-sig-auth/Cargo.toml b/aws/rust-runtime/aws-sig-auth/Cargo.toml index cb9695f7db..deece1ac30 100644 --- a/aws/rust-runtime/aws-sig-auth/Cargo.toml +++ b/aws/rust-runtime/aws-sig-auth/Cargo.toml @@ -15,6 +15,7 @@ aws-credential-types = { path = "../aws-credential-types" } aws-sigv4 = { path = "../aws-sigv4" } aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-types = { path = "../aws-types" } http = "0.2.2" tracing = "0.1" @@ -22,8 +23,9 @@ tracing = "0.1" [dev-dependencies] aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-endpoint = { path = "../aws-endpoint" } -aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types"} -tracing-test = "0.2.1" +aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } +tracing-test = "0.2.4" +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs index 74b8c65dd4..bf51ac2723 100644 --- a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs +++ b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs @@ -3,6 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +// this code is dead +#![allow(deprecated)] +#![allow(clippy::disallowed_methods)] + use crate::middleware::Signature; use aws_credential_types::Credentials; use aws_sigv4::event_stream::{sign_empty_message, sign_message}; @@ -15,6 +19,115 @@ use std::time::SystemTime; /// Event Stream SigV4 signing implementation. #[derive(Debug)] +pub struct SigV4MessageSigner { + last_signature: String, + credentials: Credentials, + signing_region: SigningRegion, + signing_service: SigningService, + time: Option, +} + +impl SigV4MessageSigner { + pub fn new( + last_signature: String, + credentials: Credentials, + signing_region: SigningRegion, + signing_service: SigningService, + time: Option, + ) -> Self { + Self { + last_signature, + credentials, + signing_region, + signing_service, + time, + } + } + + fn signing_params(&self) -> SigningParams<()> { + let mut builder = SigningParams::builder() + .access_key(self.credentials.access_key_id()) + .secret_key(self.credentials.secret_access_key()) + .region(self.signing_region.as_ref()) + .service_name(self.signing_service.as_ref()) + .time(self.time.unwrap_or_else(SystemTime::now)) + .settings(()); + builder.set_security_token(self.credentials.session_token()); + builder.build().unwrap() + } +} + +impl SignMessage for SigV4MessageSigner { + fn sign(&mut self, message: Message) -> Result { + let (signed_message, signature) = { + let params = self.signing_params(); + sign_message(&message, &self.last_signature, ¶ms).into_parts() + }; + self.last_signature = signature; + Ok(signed_message) + } + + fn sign_empty(&mut self) -> Option> { + let (signed_message, signature) = { + let params = self.signing_params(); + sign_empty_message(&self.last_signature, ¶ms).into_parts() + }; + self.last_signature = signature; + Some(Ok(signed_message)) + } +} + +#[cfg(test)] +mod tests { + use crate::event_stream::SigV4MessageSigner; + use aws_credential_types::Credentials; + use aws_smithy_eventstream::frame::{HeaderValue, Message, SignMessage}; + use aws_types::region::Region; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use std::time::{Duration, UNIX_EPOCH}; + + fn check_send_sync(value: T) -> T { + value + } + + #[test] + fn sign_message() { + let region = Region::new("us-east-1"); + let mut signer = check_send_sync(SigV4MessageSigner::new( + "initial-signature".into(), + Credentials::for_tests(), + SigningRegion::from(region), + SigningService::from_static("transcribe"), + Some(UNIX_EPOCH + Duration::new(1611160427, 0)), + )); + let mut signatures = Vec::new(); + for _ in 0..5 { + let signed = signer + .sign(Message::new(&b"identical message"[..])) + .unwrap(); + if let HeaderValue::ByteArray(signature) = signed + .headers() + .iter() + .find(|h| h.name().as_str() == ":chunk-signature") + .unwrap() + .value() + { + signatures.push(signature.clone()); + } else { + panic!("failed to get the :chunk-signature") + } + } + for i in 1..signatures.len() { + assert_ne!(signatures[i - 1], signatures[i]); + } + } +} + +// TODO(enableNewSmithyRuntimeCleanup): Delete this old implementation that was kept around to support patch releases. +#[deprecated = "use aws_sig_auth::event_stream::SigV4MessageSigner instead (this may require upgrading the smithy-rs code generator)"] +#[derive(Debug)] +/// Event Stream SigV4 signing implementation. pub struct SigV4Signer { properties: SharedPropertyBag, last_signature: Option, @@ -87,8 +200,9 @@ impl SignMessage for SigV4Signer { } } +// TODO(enableNewSmithyRuntimeCleanup): Delete this old implementation that was kept around to support patch releases. #[cfg(test)] -mod tests { +mod old_tests { use crate::event_stream::SigV4Signer; use crate::middleware::Signature; use aws_credential_types::Credentials; diff --git a/aws/rust-runtime/aws-sig-auth/src/lib.rs b/aws/rust-runtime/aws-sig-auth/src/lib.rs index 643b3ff216..90cc88a7e3 100644 --- a/aws/rust-runtime/aws-sig-auth/src/lib.rs +++ b/aws/rust-runtime/aws-sig-auth/src/lib.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(enableNewSmithyRuntimeCleanup): Deprecate this crate and replace it with empty contents. Remove references to it in the code generator. + #![allow(clippy::derive_partial_eq_without_eq)] //! AWS Signature Authentication Package diff --git a/aws/rust-runtime/aws-sig-auth/src/middleware.rs b/aws/rust-runtime/aws-sig-auth/src/middleware.rs index d7ec53454c..b901f3a856 100644 --- a/aws/rust-runtime/aws-sig-auth/src/middleware.rs +++ b/aws/rust-runtime/aws-sig-auth/src/middleware.rs @@ -5,7 +5,6 @@ use std::error::Error; use std::fmt::{Display, Formatter}; -use std::time::SystemTime; use aws_smithy_http::middleware::MapRequest; use aws_smithy_http::operation::Request; @@ -13,6 +12,7 @@ use aws_smithy_http::property_bag::PropertyBag; use aws_credential_types::Credentials; use aws_sigv4::http_request::SignableBody; +use aws_smithy_async::time::SharedTimeSource; use aws_types::region::SigningRegion; use aws_types::SigningService; @@ -20,8 +20,15 @@ use crate::signer::{ OperationSigningConfig, RequestConfig, SigV4Signer, SigningError, SigningRequirements, }; +#[cfg(feature = "sign-eventstream")] +use crate::event_stream::SigV4MessageSigner as EventStreamSigV4Signer; +#[cfg(feature = "sign-eventstream")] +use aws_smithy_eventstream::frame::DeferredSignerSender; + +// TODO(enableNewSmithyRuntimeCleanup): Delete `Signature` when switching to the orchestrator /// Container for the request signature for use in the property bag. #[non_exhaustive] +#[derive(Debug, Clone)] pub struct Signature(String); impl Signature { @@ -47,11 +54,8 @@ impl AsRef for Signature { /// - [`Credentials`](Credentials): Credentials to sign with /// - [`OperationSigningConfig`](OperationSigningConfig): Operation specific signing configuration, e.g. /// changes to URL encoding behavior, or headers that must be omitted. +/// - [`SharedTimeSource`]: The time source to use when signing the request. /// If any of these fields are missing, the middleware will return an error. -/// -/// The following fields MAY be present in the property bag: -/// - [`SystemTime`](SystemTime): The timestamp to use when signing the request. If this field is not present -/// [`SystemTime::now`](SystemTime::now) will be used. #[derive(Clone, Debug)] pub struct SigV4SigningStage { signer: SigV4Signer, @@ -145,9 +149,9 @@ fn signing_config( let payload_override = config.get::>(); let request_config = RequestConfig { request_ts: config - .get::() - .copied() - .unwrap_or_else(SystemTime::now), + .get::() + .map(|t| t.now()) + .unwrap_or_else(|| SharedTimeSource::default().now()), region, payload_override, service: signing_service, @@ -181,6 +185,22 @@ impl MapRequest for SigV4SigningStage { .signer .sign(operation_config, &request_config, &creds, &mut req) .map_err(SigningStageErrorKind::SigningFailure)?; + + // If this is an event stream operation, set up the event stream signer + #[cfg(feature = "sign-eventstream")] + if let Some(signer_sender) = config.get::() { + let time_override = config.get::().map(|ts| ts.now()); + signer_sender + .send(Box::new(EventStreamSigV4Signer::new( + signature.as_ref().into(), + creds, + request_config.region.clone(), + request_config.service.clone(), + time_override, + )) as _) + .expect("failed to send deferred signer"); + } + config.insert(signature); Ok(req) }) @@ -199,6 +219,7 @@ mod test { use aws_credential_types::Credentials; use aws_endpoint::AwsAuthStage; + use aws_smithy_async::time::SharedTimeSource; use aws_types::region::{Region, SigningRegion}; use aws_types::SigningService; @@ -234,6 +255,50 @@ mod test { assert!(signature.is_some()); } + #[cfg(feature = "sign-eventstream")] + #[test] + fn sends_event_stream_signer_for_event_stream_operations() { + use crate::event_stream::SigV4MessageSigner as EventStreamSigV4Signer; + use aws_smithy_eventstream::frame::{DeferredSigner, SignMessage}; + + let (mut deferred_signer, deferred_signer_sender) = DeferredSigner::new(); + let req = http::Request::builder() + .uri("https://test-service.test-region.amazonaws.com/") + .body(SdkBody::from("")) + .unwrap(); + let region = Region::new("us-east-1"); + let req = operation::Request::new(req) + .augment(|req, properties| { + properties.insert(region.clone()); + properties.insert::(SharedTimeSource::new( + UNIX_EPOCH + Duration::new(1611160427, 0), + )); + properties.insert(SigningService::from_static("kinesis")); + properties.insert(OperationSigningConfig::default_config()); + properties.insert(Credentials::for_tests()); + properties.insert(SigningRegion::from(region.clone())); + properties.insert(deferred_signer_sender); + Result::<_, Infallible>::Ok(req) + }) + .expect("succeeds"); + + let signer = SigV4SigningStage::new(SigV4Signer::new()); + let _ = signer.apply(req).unwrap(); + + let mut signer_for_comparison = EventStreamSigV4Signer::new( + // This is the expected SigV4 signature for the HTTP request above + "abac477b4afabf5651079e7b9a0aa6a1a3e356a7418a81d974cdae9d4c8e5441".into(), + Credentials::for_tests(), + SigningRegion::from(region), + SigningService::from_static("kinesis"), + Some(UNIX_EPOCH + Duration::new(1611160427, 0)), + ); + + let expected_signed_empty = signer_for_comparison.sign_empty().unwrap().unwrap(); + let actual_signed_empty = deferred_signer.sign_empty().unwrap().unwrap(); + assert_eq!(expected_signed_empty, actual_signed_empty); + } + // check that the endpoint middleware followed by signing middleware produce the expected result #[test] fn endpoint_plus_signer() { @@ -249,7 +314,9 @@ mod test { let req = operation::Request::new(req) .augment(|req, conf| { conf.insert(region.clone()); - conf.insert(UNIX_EPOCH + Duration::new(1611160427, 0)); + conf.insert(SharedTimeSource::new( + UNIX_EPOCH + Duration::new(1611160427, 0), + )); conf.insert(SigningService::from_static("kinesis")); conf.insert(endpoint); Result::<_, Infallible>::Ok(req) diff --git a/aws/rust-runtime/aws-sig-auth/src/signer.rs b/aws/rust-runtime/aws-sig-auth/src/signer.rs index a1d36c97ca..d71c6ecf42 100644 --- a/aws/rust-runtime/aws-sig-auth/src/signer.rs +++ b/aws/rust-runtime/aws-sig-auth/src/signer.rs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::middleware::Signature; use aws_credential_types::Credentials; use aws_sigv4::http_request::{ sign, PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignableRequest, @@ -15,6 +14,7 @@ use aws_types::SigningService; use std::fmt; use std::time::{Duration, SystemTime}; +use crate::middleware::Signature; pub use aws_sigv4::http_request::SignableBody; pub type SigningError = aws_sigv4::http_request::SigningError; diff --git a/aws/rust-runtime/aws-sigv4/Cargo.toml b/aws/rust-runtime/aws-sigv4/Cargo.toml index 90cf5f42f3..ea54025552 100644 --- a/aws/rust-runtime/aws-sigv4/Cargo.toml +++ b/aws/rust-runtime/aws-sigv4/Cargo.toml @@ -33,7 +33,7 @@ criterion = "0.4" bytes = "1" httparse = "1.5" pretty_assertions = "1.3" -proptest = "1" +proptest = "1.2" time = { version = "0.3.4", features = ["parsing"] } [target.'cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))'.dev-dependencies] diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs index f7c2692a37..221463ada2 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs @@ -774,14 +774,46 @@ mod tests { assert_eq!(creq.values.signed_headers().as_str(), "host;x-amz-date"); } - // It should exclude user-agent and x-amz-user-agent headers from presigning + // It should exclude authorization, user-agent, x-amzn-trace-id headers from presigning + #[test] + fn non_presigning_header_exclusion() { + let request = http::Request::builder() + .uri("https://some-endpoint.some-region.amazonaws.com") + .header("authorization", "test-authorization") + .header("content-type", "application/xml") + .header("content-length", "0") + .header("user-agent", "test-user-agent") + .header("x-amzn-trace-id", "test-trace-id") + .header("x-amz-user-agent", "test-user-agent") + .body("") + .unwrap(); + let request = SignableRequest::from(&request); + + let settings = SigningSettings { + signature_location: SignatureLocation::Headers, + ..Default::default() + }; + + let signing_params = signing_params(settings); + let canonical = CanonicalRequest::from(&request, &signing_params).unwrap(); + + let values = canonical.values.as_headers().unwrap(); + assert_eq!( + "content-length;content-type;host;x-amz-date;x-amz-user-agent", + values.signed_headers.as_str() + ); + } + + // It should exclude authorization, user-agent, x-amz-user-agent, x-amzn-trace-id headers from presigning #[test] fn presigning_header_exclusion() { let request = http::Request::builder() .uri("https://some-endpoint.some-region.amazonaws.com") + .header("authorization", "test-authorization") .header("content-type", "application/xml") .header("content-length", "0") .header("user-agent", "test-user-agent") + .header("x-amzn-trace-id", "test-trace-id") .header("x-amz-user-agent", "test-user-agent") .body("") .unwrap(); diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs b/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs index 023c142997..db021bbee5 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs @@ -56,4 +56,4 @@ pub use settings::{ PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignatureLocation, SigningParams, SigningSettings, UriPathNormalizationMode, }; -pub use sign::{sign, SignableBody, SignableRequest}; +pub use sign::{sign, SignableBody, SignableRequest, SigningInstructions}; diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs b/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs index bac85281df..501cd5c775 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs @@ -3,12 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use http::header::{HeaderName, USER_AGENT}; +use http::header::{HeaderName, AUTHORIZATION, USER_AGENT}; use std::time::Duration; /// HTTP signing parameters pub type SigningParams<'a> = crate::SigningParams<'a, SigningSettings>; +const HEADER_NAME_X_RAY_TRACE_ID: &str = "x-amzn-trace-id"; + /// HTTP-specific signing settings #[derive(Debug, PartialEq)] #[non_exhaustive] @@ -97,15 +99,30 @@ pub enum SessionTokenMode { impl Default for SigningSettings { fn default() -> Self { - // The user agent header should not be signed because it may be altered by proxies - const EXCLUDED_HEADERS: [HeaderName; 1] = [USER_AGENT]; - + // Headers that are potentially altered by proxies or as a part of standard service operations. + // Reference: + // Go SDK: + // Java SDK: + // JS SDK: + // There is no single source of truth for these available, so this uses the minimum common set of the excluded options. + // Instantiate this every time, because SigningSettings takes a Vec (which cannot be const); + let excluded_headers = Some( + [ + // This header is calculated as part of the signing process, so if it's present, discard it + AUTHORIZATION, + // Changes when sent by proxy + USER_AGENT, + // Changes based on the request from the client + HeaderName::from_static(HEADER_NAME_X_RAY_TRACE_ID), + ] + .to_vec(), + ); Self { percent_encoding_mode: PercentEncodingMode::Double, payload_checksum_kind: PayloadChecksumKind::NoHeader, signature_location: SignatureLocation::Headers, expires_in: None, - excluded_headers: Some(EXCLUDED_HEADERS.to_vec()), + excluded_headers, uri_path_normalization_mode: UriPathNormalizationMode::Enabled, session_token_mode: SessionTokenMode::Include, } diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs index 97a41ad724..aaeda06bb7 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs @@ -103,6 +103,7 @@ pub enum SignableBody<'a> { StreamingUnsignedPayloadTrailer, } +/// Instructions for applying a signature to an HTTP request. #[derive(Debug)] pub struct SigningInstructions { headers: Option>, @@ -117,20 +118,27 @@ impl SigningInstructions { Self { headers, params } } + /// Returns a reference to the headers that should be added to the request. pub fn headers(&self) -> Option<&HeaderMap> { self.headers.as_ref() } + + /// Returns the headers and sets the internal value to `None`. pub fn take_headers(&mut self) -> Option> { self.headers.take() } + /// Returns a reference to the query parameters that should be added to the request. pub fn params(&self) -> Option<&Vec<(&'static str, Cow<'static, str>)>> { self.params.as_ref() } + + /// Returns the query parameters and sets the internal value to `None`. pub fn take_params(&mut self) -> Option)>> { self.params.take() } + /// Applies the instructions to the given `request`. pub fn apply_to_request(mut self, request: &mut http::Request) { if let Some(new_headers) = self.take_headers() { for (name, value) in new_headers.into_iter() { @@ -256,7 +264,7 @@ fn calculate_signing_headers<'a>( // Step 4: https://docs.aws.amazon.com/en_pv/general/latest/gr/sigv4-add-signature-to-request.html let values = creq.values.as_headers().expect("signing with headers"); let mut headers = HeaderMap::new(); - add_header(&mut headers, header::X_AMZ_DATE, &values.date_time); + add_header(&mut headers, header::X_AMZ_DATE, &values.date_time, false); headers.insert( "authorization", build_authorization_header(params.access_key, &creq, sts, &signature), @@ -266,18 +274,26 @@ fn calculate_signing_headers<'a>( &mut headers, header::X_AMZ_CONTENT_SHA_256, &values.content_sha256, + false, ); } if let Some(security_token) = params.security_token { - add_header(&mut headers, header::X_AMZ_SECURITY_TOKEN, security_token); + add_header( + &mut headers, + header::X_AMZ_SECURITY_TOKEN, + security_token, + true, + ); } Ok(SigningOutput::new(headers, signature)) } -fn add_header(map: &mut HeaderMap, key: &'static str, value: &str) { - map.insert(key, HeaderValue::try_from(value).expect(key)); +fn add_header(map: &mut HeaderMap, key: &'static str, value: &str, sensitive: bool) { + let mut value = HeaderValue::try_from(value).expect(key); + value.set_sensitive(sensitive); + map.insert(key, value); } // add signature to authorization header @@ -595,7 +611,7 @@ mod tests { security_token: None, region: "us-east-1", service_name: "foo", - time: std::time::SystemTime::now(), + time: std::time::SystemTime::UNIX_EPOCH, settings, }; @@ -624,7 +640,7 @@ mod tests { security_token: None, region: "us-east-1", service_name: "foo", - time: std::time::SystemTime::now(), + time: std::time::SystemTime::UNIX_EPOCH, settings, }; diff --git a/aws/rust-runtime/aws-sigv4/src/lib.rs b/aws/rust-runtime/aws-sigv4/src/lib.rs index 14d7a7b5fd..d20df23994 100644 --- a/aws/rust-runtime/aws-sigv4/src/lib.rs +++ b/aws/rust-runtime/aws-sigv4/src/lib.rs @@ -15,6 +15,7 @@ unreachable_pub )] +use std::fmt; use std::time::SystemTime; pub mod sign; @@ -29,7 +30,6 @@ pub mod http_request; /// Parameters to use when signing. #[non_exhaustive] -#[derive(Debug)] pub struct SigningParams<'a, S> { /// Access Key ID to use. pub(crate) access_key: &'a str, @@ -49,6 +49,32 @@ pub struct SigningParams<'a, S> { pub(crate) settings: S, } +impl<'a, S> SigningParams<'a, S> { + /// Returns the region that will be used to sign + pub fn region(&self) -> &str { + self.region + } + + /// Returns the service name that will be used to sign + pub fn service_name(&self) -> &str { + self.service_name + } +} + +impl<'a, S: fmt::Debug> fmt::Debug for SigningParams<'a, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SigningParams") + .field("access_key", &"** redacted **") + .field("secret_key", &"** redacted **") + .field("security_token", &"** redacted **") + .field("region", &self.region) + .field("service_name", &self.service_name) + .field("time", &self.time) + .field("settings", &self.settings) + .finish() + } +} + impl<'a, S: Default> SigningParams<'a, S> { /// Returns a builder that can create new `SigningParams`. pub fn builder() -> signing_params::Builder<'a, S> { diff --git a/aws/rust-runtime/aws-types/Cargo.toml b/aws/rust-runtime/aws-types/Cargo.toml index 31d72bd1ec..eb0b527f42 100644 --- a/aws/rust-runtime/aws-types/Cargo.toml +++ b/aws/rust-runtime/aws-types/Cargo.toml @@ -19,15 +19,15 @@ aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } tracing = "0.1" http = "0.2.6" -# cargo does not support optional test dependencies, so to completely disable rustls when -# the native-tls feature is enabled, we need to add the webpki-roots feature here. +# cargo does not support optional test dependencies, so to completely disable rustls +# we need to add the webpki-roots feature here. # https://github.com/rust-lang/cargo/issues/1596 -hyper-rustls = { version = "0.23.0", optional = true, features = ["rustls-native-certs", "http2", "webpki-roots"] } +hyper-rustls = { version = "0.24", optional = true, features = ["rustls-native-certs", "http2", "webpki-roots"] } [dev-dependencies] futures-util = { version = "0.3.16", default-features = false } http = "0.2.4" -tracing-test = "0.2.1" +tracing-test = "0.2.4" [build-dependencies] rustc_version = "0.4.0" diff --git a/aws/rust-runtime/aws-types/external-types.toml b/aws/rust-runtime/aws-types/external-types.toml index 71ce4acd8a..e43510d69e 100644 --- a/aws/rust-runtime/aws-types/external-types.toml +++ b/aws/rust-runtime/aws-types/external-types.toml @@ -1,12 +1,17 @@ allowed_external_types = [ "aws_credential_types::cache::CredentialsCache", "aws_credential_types::provider::SharedCredentialsProvider", - "aws_smithy_async::rt::sleep::AsyncSleep", + "aws_smithy_async::rt::sleep::SharedAsyncSleep", + "aws_smithy_async::time::SharedTimeSource", + "aws_smithy_async::time::TimeSource", "aws_smithy_client::http_connector", "aws_smithy_client::http_connector::HttpConnector", "aws_smithy_http::endpoint::Endpoint", "aws_smithy_http::endpoint::EndpointPrefix", "aws_smithy_http::endpoint::error::InvalidEndpointError", + "aws_smithy_types::config_bag::storable::Storable", + "aws_smithy_types::config_bag::storable::StoreReplace", + "aws_smithy_types::config_bag::storable::Storer", "aws_smithy_types::retry::RetryConfig", "aws_smithy_types::timeout::TimeoutConfig", "http::uri::Uri", diff --git a/aws/rust-runtime/aws-types/src/app_name.rs b/aws/rust-runtime/aws-types/src/app_name.rs index 70843e19bd..25555eab64 100644 --- a/aws/rust-runtime/aws-types/src/app_name.rs +++ b/aws/rust-runtime/aws-types/src/app_name.rs @@ -5,6 +5,7 @@ //! New-type for a configurable app name. +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::borrow::Cow; use std::error::Error; use std::fmt; @@ -38,6 +39,10 @@ impl fmt::Display for AppName { } } +impl Storable for AppName { + type Storer = StoreReplace; +} + impl AppName { /// Creates a new app name. /// diff --git a/aws/rust-runtime/aws-types/src/endpoint_config.rs b/aws/rust-runtime/aws-types/src/endpoint_config.rs new file mode 100644 index 0000000000..562051f530 --- /dev/null +++ b/aws/rust-runtime/aws-types/src/endpoint_config.rs @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Newtypes for endpoint-related parameters +//! +//! Parameters require newtypes so they have distinct types when stored in layers in config bag. + +use aws_smithy_types::config_bag::{Storable, StoreReplace}; + +/// Newtype for `use_fips` +#[derive(Clone, Debug)] +pub struct UseFips(pub bool); +impl Storable for UseFips { + type Storer = StoreReplace; +} + +/// Newtype for `use_dual_stack` +#[derive(Clone, Debug)] +pub struct UseDualStack(pub bool); +impl Storable for UseDualStack { + type Storer = StoreReplace; +} + +/// Newtype for `endpoint_url` +#[derive(Clone, Debug)] +pub struct EndpointUrl(pub String); +impl Storable for EndpointUrl { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-types/src/lib.rs b/aws/rust-runtime/aws-types/src/lib.rs index 27860df1be..ed4e635037 100644 --- a/aws/rust-runtime/aws-types/src/lib.rs +++ b/aws/rust-runtime/aws-types/src/lib.rs @@ -16,6 +16,7 @@ pub mod app_name; pub mod build_metadata; +pub mod endpoint_config; #[doc(hidden)] pub mod os_shim_internal; pub mod region; @@ -24,6 +25,7 @@ pub mod sdk_config; pub use aws_smithy_client::http_connector; pub use sdk_config::SdkConfig; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::borrow::Cow; /// The name of the service used to sign this request @@ -55,3 +57,7 @@ impl From<&'static str> for SigningService { Self::from_static(service) } } + +impl Storable for SigningService { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-types/src/region.rs b/aws/rust-runtime/aws-types/src/region.rs index 3f053d0acb..a58f809a70 100644 --- a/aws/rust-runtime/aws-types/src/region.rs +++ b/aws/rust-runtime/aws-types/src/region.rs @@ -5,6 +5,7 @@ //! Region type for determining the endpoint to send requests to. +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::borrow::Cow; use std::fmt::{Display, Formatter}; @@ -35,6 +36,10 @@ impl Display for Region { } } +impl Storable for Region { + type Storer = StoreReplace; +} + impl Region { /// Creates a new `Region` from the given string. pub fn new(region: impl Into>) -> Self { @@ -77,3 +82,7 @@ impl SigningRegion { SigningRegion(Cow::Borrowed(region)) } } + +impl Storable for SigningRegion { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs index 3c6c69b612..08e49469ba 100644 --- a/aws/rust-runtime/aws-types/src/sdk_config.rs +++ b/aws/rust-runtime/aws-types/src/sdk_config.rs @@ -9,11 +9,10 @@ //! //! This module contains an shared configuration representation that is agnostic from a specific service. -use std::sync::Arc; - use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -40,6 +39,8 @@ If no dual-stack endpoint is available the request MAY return an error. **Note**: Some services do not offer dual-stack as a configurable parameter (e.g. Code Catalyst). For these services, this setting has no effect" }; + + (time_source) => { "The time source use to use for this client. This only needs to be required for creating deterministic tests or platforms where `SystemTime::now()` is not supported." }; } } @@ -52,7 +53,8 @@ pub struct SdkConfig { region: Option, endpoint_url: Option, retry_config: Option, - sleep_impl: Option>, + sleep_impl: Option, + time_source: Option, timeout_config: Option, http_connector: Option, use_fips: Option, @@ -72,7 +74,8 @@ pub struct Builder { region: Option, endpoint_url: Option, retry_config: Option, - sleep_impl: Option>, + sleep_impl: Option, + time_source: Option, timeout_config: Option, http_connector: Option, use_fips: Option, @@ -114,7 +117,7 @@ impl Builder { self } - /// Set the endpoint url to use when making requests. + /// Set the endpoint URL to use when making requests. /// # Examples /// ``` /// use aws_types::SdkConfig; @@ -125,7 +128,7 @@ impl Builder { self } - /// Set the endpoint url to use when making requests. + /// Set the endpoint URL to use when making requests. pub fn set_endpoint_url(&mut self, endpoint_url: Option) -> &mut Self { self.endpoint_url = endpoint_url; self @@ -236,8 +239,7 @@ impl Builder { /// # Examples /// /// ```rust - /// use std::sync::Arc; - /// use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; + /// use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; /// use aws_types::SdkConfig; /// /// ##[derive(Debug)] @@ -249,10 +251,10 @@ impl Builder { /// } /// } /// - /// let sleep_impl = Arc::new(ForeverSleep); + /// let sleep_impl = SharedAsyncSleep::new(ForeverSleep); /// let config = SdkConfig::builder().sleep_impl(sleep_impl).build(); /// ``` - pub fn sleep_impl(mut self, sleep_impl: Arc) -> Self { + pub fn sleep_impl(mut self, sleep_impl: SharedAsyncSleep) -> Self { self.set_sleep_impl(Some(sleep_impl)); self } @@ -265,7 +267,7 @@ impl Builder { /// /// # Examples /// ```rust - /// # use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; + /// # use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; /// # use aws_types::sdk_config::{Builder, SdkConfig}; /// #[derive(Debug)] /// pub struct ForeverSleep; @@ -277,7 +279,7 @@ impl Builder { /// } /// /// fn set_never_ending_sleep_impl(builder: &mut Builder) { - /// let sleep_impl = std::sync::Arc::new(ForeverSleep); + /// let sleep_impl = SharedAsyncSleep::new(ForeverSleep); /// builder.set_sleep_impl(Some(sleep_impl)); /// } /// @@ -285,7 +287,7 @@ impl Builder { /// set_never_ending_sleep_impl(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_sleep_impl(&mut self, sleep_impl: Option>) -> &mut Self { + pub fn set_sleep_impl(&mut self, sleep_impl: Option) -> &mut Self { self.sleep_impl = sleep_impl; self } @@ -442,7 +444,7 @@ impl Builder { /// use std::time::Duration; /// use aws_smithy_client::hyper_ext; /// use aws_smithy_client::http_connector::ConnectorSettings; - /// use aws_types::sdk_config::{SdkConfig, Builder}; + /// use aws_types::sdk_config::{Builder, SdkConfig}; /// /// fn override_http_connector(builder: &mut Builder) { /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() @@ -499,6 +501,18 @@ impl Builder { self } + #[doc = docs_for!(time_source)] + pub fn time_source(mut self, time_source: impl TimeSource + 'static) -> Self { + self.set_time_source(Some(SharedTimeSource::new(time_source))); + self + } + + #[doc = docs_for!(time_source)] + pub fn set_time_source(&mut self, time_source: Option) -> &mut Self { + self.time_source = time_source; + self + } + /// Build a [`SdkConfig`](SdkConfig) from this builder pub fn build(self) -> SdkConfig { SdkConfig { @@ -513,6 +527,7 @@ impl Builder { http_connector: self.http_connector, use_fips: self.use_fips, use_dual_stack: self.use_dual_stack, + time_source: self.time_source, } } } @@ -540,7 +555,7 @@ impl SdkConfig { #[doc(hidden)] /// Configured sleep implementation - pub fn sleep_impl(&self) -> Option> { + pub fn sleep_impl(&self) -> Option { self.sleep_impl.clone() } @@ -550,8 +565,13 @@ impl SdkConfig { } /// Configured credentials provider - pub fn credentials_provider(&self) -> Option<&SharedCredentialsProvider> { - self.credentials_provider.as_ref() + pub fn credentials_provider(&self) -> Option { + self.credentials_provider.clone() + } + + /// Configured time source + pub fn time_source(&self) -> Option { + self.time_source.clone() } /// Configured app name diff --git a/aws/rust-runtime/clippy.toml b/aws/rust-runtime/clippy.toml new file mode 120000 index 0000000000..0cbd6319b2 --- /dev/null +++ b/aws/rust-runtime/clippy.toml @@ -0,0 +1 @@ +../../clippy-root.toml \ No newline at end of file diff --git a/aws/sdk-adhoc-test/build.gradle.kts b/aws/sdk-adhoc-test/build.gradle.kts index 98ad953105..2e09902e5d 100644 --- a/aws/sdk-adhoc-test/build.gradle.kts +++ b/aws/sdk-adhoc-test/build.gradle.kts @@ -37,6 +37,8 @@ dependencies { implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") } +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "orchestrator" + val allCodegenTests = listOf( CodegenTest( "com.amazonaws.apigateway#BackplaneControlService", @@ -46,7 +48,7 @@ val allCodegenTests = listOf( , "codegen": { "includeFluentClient": false, - "enableNewCrateOrganizationScheme": true + "enableNewSmithyRuntime": "${getSmithyRuntimeMode()}" }, "customizationConfig": { "awsSdk": { @@ -63,7 +65,7 @@ val allCodegenTests = listOf( , "codegen": { "includeFluentClient": false, - "enableNewCrateOrganizationScheme": true + "enableNewSmithyRuntime": "${getSmithyRuntimeMode()}" }, "customizationConfig": { "awsSdk": { diff --git a/aws/sdk-adhoc-test/models/apigateway-rules.smithy b/aws/sdk-adhoc-test/models/apigateway-rules.smithy index fa609c9860..3e5bb310d3 100644 --- a/aws/sdk-adhoc-test/models/apigateway-rules.smithy +++ b/aws/sdk-adhoc-test/models/apigateway-rules.smithy @@ -1,8 +1,16 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// The API Gateway model is coming from Smithy's protocol tests, and includes an `Accept` header test: +// https://github.com/awslabs/smithy/blob/2f6553ff39e6bba9edc644ef5832661821785319/smithy-aws-protocol-tests/model/restJson1/services/apigateway.smithy#L30-L43 + $version: "1.0" namespace com.amazonaws.apigateway use smithy.rules#endpointRuleSet + +// Add an endpoint ruleset to the Smithy protocol test API Gateway model so that the code generator doesn't fail apply BackplaneControlService @endpointRuleSet({ "version": "1.0", "rules": [{ diff --git a/aws/sdk-codegen/build.gradle.kts b/aws/sdk-codegen/build.gradle.kts index bb6f2925a7..b7a6471bed 100644 --- a/aws/sdk-codegen/build.gradle.kts +++ b/aws/sdk-codegen/build.gradle.kts @@ -87,12 +87,11 @@ if (isTestingEnabled.toBoolean()) { tasks.test { useJUnitPlatform() testLogging { - events("passed", "skipped", "failed") + events("failed") exceptionFormat = TestExceptionFormat.FULL showCauses = true showExceptions = true showStackTraces = true - showStandardStreams = true } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCargoDependency.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCargoDependency.kt index 655efd8b8a..c0127ae1e9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCargoDependency.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCargoDependency.kt @@ -13,10 +13,12 @@ fun RuntimeConfig.awsRuntimeCrate(name: String, features: Set = setOf()) CargoDependency(name, awsRoot().crateLocation(null), features = features) object AwsCargoDependency { - fun awsCredentialTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-credential-types") fun awsConfig(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-config") + fun awsCredentialTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-credential-types") fun awsEndpoint(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-endpoint") fun awsHttp(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-http") + fun awsRuntime(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-runtime") + fun awsRuntimeApi(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-runtime-api") fun awsSigAuth(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-sig-auth") fun awsSigAuthEventStream(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-sig-auth", setOf("sign-eventstream")) fun awsSigv4(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-sigv4") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt index 802b55b04a..952cb35e60 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt @@ -19,7 +19,9 @@ import software.amazon.smithy.rustsdk.customize.route53.Route53Decorator import software.amazon.smithy.rustsdk.customize.s3.S3Decorator import software.amazon.smithy.rustsdk.customize.s3.S3ExtendedRequestIdDecorator import software.amazon.smithy.rustsdk.customize.s3control.S3ControlDecorator +import software.amazon.smithy.rustsdk.customize.sso.SSODecorator import software.amazon.smithy.rustsdk.customize.sts.STSDecorator +import software.amazon.smithy.rustsdk.customize.timestream.TimestreamDecorator import software.amazon.smithy.rustsdk.endpoints.AwsEndpointsStdLib import software.amazon.smithy.rustsdk.endpoints.OperationInputTestDecorator import software.amazon.smithy.rustsdk.endpoints.RequireEndpointRules @@ -32,6 +34,7 @@ val DECORATORS: List = listOf( RegionDecorator(), RequireEndpointRules(), UserAgentDecorator(), + SigV4AuthDecorator(), SigV4SigningDecorator(), HttpRequestChecksumDecorator(), HttpResponseChecksumDecorator(), @@ -50,6 +53,9 @@ val DECORATORS: List = listOf( OperationInputTestDecorator(), AwsRequestIdDecorator(), DisabledAuthDecorator(), + RecursionDetectionDecorator(), + InvocationIdDecorator(), + RetryInformationHeaderDecorator(), ), // Service specific decorators @@ -63,6 +69,9 @@ val DECORATORS: List = listOf( ), S3ControlDecorator().onlyApplyTo("com.amazonaws.s3control#AWSS3ControlServiceV20180820"), STSDecorator().onlyApplyTo("com.amazonaws.sts#AWSSecurityTokenServiceV20110615"), + SSODecorator().onlyApplyTo("com.amazonaws.sso#SWBPortalService"), + TimestreamDecorator().onlyApplyTo("com.amazonaws.timestreamwrite#Timestream_20181101"), + TimestreamDecorator().onlyApplyTo("com.amazonaws.timestreamquery#Timestream_20181101"), // Only build docs-rs for linux to reduce load on docs.rs listOf( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt index a810b3e8a5..f167015ac2 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rawTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocSection import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations @@ -85,6 +86,10 @@ class AwsCrateDocsDecorator : ClientCodegenDecorator { SdkSettings.from(codegenContext.settings).generateReadme } +sealed class DocSection(name: String) : AdHocSection(name) { + data class CreateClient(val crateName: String, val clientName: String = "client", val indent: String) : DocSection("CustomExample") +} + internal class AwsCrateDocGenerator(private val codegenContext: ClientCodegenContext) { private val logger: Logger = Logger.getLogger(javaClass.name) private val awsConfigVersion by lazy { @@ -154,8 +159,7 @@ internal class AwsCrateDocGenerator(private val codegenContext: ClientCodegenCon ##[#{tokio}::main] async fn main() -> Result<(), $shortModuleName::Error> { - let config = #{aws_config}::load_from_env().await; - let client = $shortModuleName::Client::new(&config); + #{constructClient} // ... make some calls with the client @@ -171,6 +175,7 @@ internal class AwsCrateDocGenerator(private val codegenContext: ClientCodegenCon true -> AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency().toType() else -> writable { rust("aws_config") } }, + "constructClient" to AwsDocs.constructClient(codegenContext, indent = " "), ) template( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt new file mode 100644 index 0000000000..8e15437aa3 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -0,0 +1,165 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.CustomizableOperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.CustomizableOperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType + +class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : + CustomizableOperationCustomization() { + private val codegenScope = arrayOf( + *RuntimeType.preludeScope, + "AwsUserAgent" to AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent::AwsUserAgent"), + "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig), + "ConfigBag" to RuntimeType.configBag(runtimeConfig), + "http" to CargoDependency.Http.toType(), + "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::SharedInterceptor"), + "SharedTimeSource" to CargoDependency.smithyAsync(runtimeConfig).toType().resolve("time::SharedTimeSource"), + "StaticRuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::StaticRuntimePlugin"), + "StaticTimeSource" to CargoDependency.smithyAsync(runtimeConfig).toType().resolve("time::StaticTimeSource"), + "TestParamsSetterInterceptor" to testParamsSetterInterceptor(), + ) + + // TODO(enableNewSmithyRuntimeCleanup): Delete this once test helpers on `CustomizableOperation` have been removed + private fun testParamsSetterInterceptor(): RuntimeType = RuntimeType.forInlineFun("TestParamsSetterInterceptor", ClientRustModule.Client.customize) { + rustTemplate( + """ + mod test_params_setter_interceptor { + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; + use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::ConfigBag; + use std::fmt; + + pub(super) struct TestParamsSetterInterceptor { f: F } + + impl fmt::Debug for TestParamsSetterInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TestParamsSetterInterceptor") + } + } + + impl TestParamsSetterInterceptor { + pub fn new(f: F) -> Self { Self { f } } + } + + impl Interceptor for TestParamsSetterInterceptor + where + F: Fn(&mut BeforeTransmitInterceptorContextMut<'_>, &mut ConfigBag) + Send + Sync + 'static, + { + fn name(&self) -> &'static str { + "TestParamsSetterInterceptor" + } + + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + (self.f)(context, cfg); + Ok(()) + } + } + } + use test_params_setter_interceptor::TestParamsSetterInterceptor; + """, + *codegenScope, + ) + } + + override fun section(section: CustomizableOperationSection): Writable = + writable { + if (section is CustomizableOperationSection.CustomizableOperationImpl) { + if (section.isRuntimeModeOrchestrator) { + // TODO(enableNewSmithyRuntimeCleanup): Delete these utilities + rustTemplate( + """ + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn request_time_for_tests(self, request_time: ::std::time::SystemTime) -> Self { + self.runtime_plugin( + #{StaticRuntimePlugin}::new() + .with_runtime_components( + #{RuntimeComponentsBuilder}::new("request_time_for_tests") + .with_time_source(Some(#{SharedTimeSource}::new(#{StaticTimeSource}::new(request_time)))) + ) + ) + } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn user_agent_for_tests(mut self) -> Self { + let interceptor = #{TestParamsSetterInterceptor}::new(|context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, _: &mut #{ConfigBag}| { + let headers = context.request_mut().headers_mut(); + let user_agent = #{AwsUserAgent}::for_tests(); + headers.insert( + #{http}::header::USER_AGENT, + #{http}::HeaderValue::try_from(user_agent.ua_header()).unwrap(), + ); + headers.insert( + #{http}::HeaderName::from_static("x-amz-user-agent"), + #{http}::HeaderValue::try_from(user_agent.aws_ua_header()).unwrap(), + ); + }); + self.interceptors.push(#{SharedInterceptor}::new(interceptor)); + self + } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn remove_invocation_id_for_tests(mut self) -> Self { + let interceptor = #{TestParamsSetterInterceptor}::new(|context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, _: &mut #{ConfigBag}| { + context.request_mut().headers_mut().remove("amz-sdk-invocation-id"); + }); + self.interceptors.push(#{SharedInterceptor}::new(interceptor)); + self + } + """, + *codegenScope, + ) + } else { + // TODO(enableNewSmithyRuntimeCleanup): Delete this branch when middleware is no longer used + rustTemplate( + """ + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { + self.operation.properties_mut().insert( + #{SharedTimeSource}::new(#{StaticTimeSource}::new(request_time)) + ); + self + } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn user_agent_for_tests(mut self) -> Self { + self.operation.properties_mut().insert(#{AwsUserAgent}::for_tests()); + self + } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn remove_invocation_id_for_tests(self) -> Self { + self + } + """, + *codegenScope, + ) + } + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt index a8dcdc33d8..7166ce1144 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt @@ -9,6 +9,9 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docsTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizationsOrElse import software.amazon.smithy.rust.codegen.core.util.toSnakeCase object AwsDocs { @@ -23,6 +26,26 @@ object AwsDocs { ShapeId.from("com.amazonaws.sso#SWBPortalService"), ).contains(codegenContext.serviceShape.id) + fun constructClient(codegenContext: ClientCodegenContext, indent: String): Writable { + val crateName = codegenContext.moduleName.toSnakeCase() + return writable { + writeCustomizationsOrElse( + codegenContext.rootDecorator.extraSections(codegenContext), + DocSection.CreateClient(crateName = crateName, indent = indent), + ) { + if (canRelyOnAwsConfig(codegenContext)) { + addDependency(AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency()) + } + rustTemplate( + """ + let config = aws_config::load_from_env().await; + let client = $crateName::Client::new(&config); + """.trimIndent().prependIndent(indent), + ) + } + } + } + fun clientConstructionDocs(codegenContext: ClientCodegenContext): Writable = { if (canRelyOnAwsConfig(codegenContext)) { val crateName = codegenContext.moduleName.toSnakeCase() @@ -40,8 +63,7 @@ object AwsDocs { In the simplest case, creating a client looks as follows: ```rust,no_run ## async fn wrapper() { - let config = #{aws_config}::load_from_env().await; - let client = $crateName::Client::new(&config); + #{constructClient} ## } ``` @@ -76,6 +98,7 @@ object AwsDocs { [builder pattern]: https://rust-lang.github.io/api-guidelines/type-safety.html##builders-enable-construction-of-complex-values-c-builder """.trimIndent(), "aws_config" to AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency().toType(), + "constructClient" to constructClient(codegenContext, indent = ""), ) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index 5fbe721e1e..30340e61cc 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -5,19 +5,19 @@ package software.amazon.smithy.rustsdk -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientDocs import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerics import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.NoClientGenerics +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.renderCustomizableOperationSend +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.DefaultProtocolTestGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Feature -import software.amazon.smithy.rust.codegen.core.rustlang.GenericTypeArg -import software.amazon.smithy.rust.codegen.core.rustlang.RustGenerics import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -40,7 +40,6 @@ private class Types(runtimeConfig: RuntimeConfig) { val awsTypes = AwsRuntimeType.awsTypes(runtimeConfig) val connectorError = smithyHttp.resolve("result::ConnectorError") val connectorSettings = smithyClient.resolve("http_connector::ConnectorSettings") - val defaultMiddleware = runtimeConfig.defaultMiddleware() val dynConnector = smithyClient.resolve("erase::DynConnector") val dynMiddleware = smithyClient.resolve("erase::DynMiddleware") val retryConfig = smithyTypes.resolve("retry::RetryConfig") @@ -50,37 +49,6 @@ private class Types(runtimeConfig: RuntimeConfig) { val timeoutConfig = smithyTypes.resolve("timeout::TimeoutConfig") } -private class AwsClientGenerics(private val types: Types) : FluentClientGenerics { - /** Declaration with defaults set */ - override val decl = writable { } - - /** Instantiation of the Smithy client generics */ - override val smithyInst = writable { - rustTemplate( - "<#{DynConnector}, #{DynMiddleware}<#{DynConnector}>>", - "DynConnector" to types.dynConnector, - "DynMiddleware" to types.dynMiddleware, - ) - } - - /** Instantiation */ - override val inst = "" - - /** Trait bounds */ - override val bounds = writable { } - - /** Bounds for generated `send()` functions */ - override fun sendBounds( - operation: Symbol, - operationOutput: Symbol, - operationError: Symbol, - retryClassifier: RuntimeType, - ): Writable = - writable { } - - override fun toRustGenerics() = RustGenerics() -} - class AwsFluentClientDecorator : ClientCodegenDecorator { override val name: String = "FluentClient" @@ -90,26 +58,25 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val runtimeConfig = codegenContext.runtimeConfig val types = Types(runtimeConfig) - val generics = AwsClientGenerics(types) + val generics = NoClientGenerics(runtimeConfig) FluentClientGenerator( codegenContext, reexportSmithyClientBuilder = false, generics = generics, customizations = listOf( - AwsPresignedFluentBuilderMethod(runtimeConfig), + AwsPresignedFluentBuilderMethod(codegenContext), AwsFluentClientDocs(codegenContext), ), retryClassifier = AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier"), - ).render(rustCrate) + ).render(rustCrate, listOf(CustomizableOperationTestHelpers(runtimeConfig))) rustCrate.withModule(ClientRustModule.Client.customize) { - renderCustomizableOperationSendMethod(runtimeConfig, generics, this) + renderCustomizableOperationSend(codegenContext, generics, this) } rustCrate.withModule(ClientRustModule.client) { - AwsFluentClientExtensions(types).render(this) + AwsFluentClientExtensions(codegenContext, types).render(this) } val awsSmithyClient = "aws-smithy-client" rustCrate.mergeFeature(Feature("rustls", default = true, listOf("$awsSmithyClient/rustls"))) - rustCrate.mergeFeature(Feature("native-tls", default = false, listOf("$awsSmithyClient/native-tls"))) } override fun libRsCustomizations( @@ -127,15 +94,46 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { } } } + + override fun protocolTestGenerator( + codegenContext: ClientCodegenContext, + baseGenerator: ProtocolTestGenerator, + ): ProtocolTestGenerator = DefaultProtocolTestGenerator( + codegenContext, + baseGenerator.protocolSupport, + baseGenerator.operationShape, + renderClientCreation = { params -> + rust("let mut ${params.configBuilderName} = ${params.configBuilderName};") + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rust("""${params.configBuilderName}.set_region(Some(crate::config::Region::new("us-east-1")));""") + } else { + rust( + """ + // If the test case was missing endpoint parameters, default a region so it doesn't fail + if ${params.configBuilderName}.region.is_none() { + ${params.configBuilderName}.set_region(Some(crate::config::Region::new("us-east-1"))); + } + """, + ) + } + rustTemplate( + """ + let config = ${params.configBuilderName}.http_connector(${params.connectorName}).build(); + let ${params.clientName} = #{Client}::from_conf(config); + """, + "Client" to ClientRustModule.root.toType().resolve("Client"), + ) + }, + ) } -private class AwsFluentClientExtensions(types: Types) { +private class AwsFluentClientExtensions(private val codegenContext: ClientCodegenContext, private val types: Types) { private val codegenScope = arrayOf( + "Arc" to RuntimeType.Arc, "ConnectorError" to types.connectorError, + "ConnectorSettings" to types.connectorSettings, "DynConnector" to types.dynConnector, "DynMiddleware" to types.dynMiddleware, - "ConnectorSettings" to types.connectorSettings, - "Middleware" to types.defaultMiddleware, "RetryConfig" to types.retryConfig, "SmithyConnector" to types.smithyConnector, "TimeoutConfig" to types.timeoutConfig, @@ -159,65 +157,72 @@ private class AwsFluentClientExtensions(types: Types) { pub fn new(sdk_config: &#{aws_types}::sdk_config::SdkConfig) -> Self { Self::from_conf(sdk_config.into()) } - - /// Creates a new client from the service [`Config`](crate::Config). - /// - /// ## Panics - /// - /// - This method will panic if the `conf` is missing an async sleep implementation. If you experience this panic, set - /// the `sleep_impl` on the Config passed into this function to fix it. - /// - This method will panic if the `conf` is missing an HTTP connector. If you experience this panic, set the - /// `http_connector` on the Config passed into this function to fix it. - pub fn from_conf(conf: crate::Config) -> Self { - let retry_config = conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); - let timeout_config = conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); - let sleep_impl = conf.sleep_impl(); - if (retry_config.has_retry() || timeout_config.has_timeouts()) && sleep_impl.is_none() { - panic!("An async sleep implementation is required for retries or timeouts to work. \ - Set the `sleep_impl` on the Config passed into this function to fix this panic."); - } - - let connector = conf.http_connector().and_then(|c| { - let timeout_config = conf - .timeout_config() - .cloned() - .unwrap_or_else(#{TimeoutConfig}::disabled); - let connector_settings = #{ConnectorSettings}::from_timeout_config( - &timeout_config, - ); - c.connector(&connector_settings, conf.sleep_impl()) - }); - - let builder = #{SmithyClientBuilder}::new(); - - let builder = match connector { - // Use provided connector - Some(c) => builder.connector(c), - None =>{ - ##[cfg(any(feature = "rustls", feature = "native-tls"))] - { - // Use default connector based on enabled features - builder.dyn_https_connector(#{ConnectorSettings}::from_timeout_config(&timeout_config)) - } - ##[cfg(not(any(feature = "rustls", feature = "native-tls")))] - { - panic!("No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this."); - } - } - }; - let mut builder = builder - .middleware(#{DynMiddleware}::new(#{Middleware}::new())) - .reconnect_mode(retry_config.reconnect_mode()) - .retry_config(retry_config.into()) - .operation_timeout_config(timeout_config.into()); - builder.set_sleep_impl(sleep_impl); - let client = builder.build(); - - Self { handle: std::sync::Arc::new(Handle { client, conf }) } - } """, *codegenScope, ) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + /// Creates a new client from the service [`Config`](crate::Config). + /// + /// ## Panics + /// + /// - This method will panic if the `conf` is missing an async sleep implementation. If you experience this panic, set + /// the `sleep_impl` on the Config passed into this function to fix it. + /// - This method will panic if the `conf` is missing an HTTP connector. If you experience this panic, set the + /// `http_connector` on the Config passed into this function to fix it. + pub fn from_conf(conf: crate::Config) -> Self { + let retry_config = conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); + let timeout_config = conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); + let sleep_impl = conf.sleep_impl(); + if (retry_config.has_retry() || timeout_config.has_timeouts()) && sleep_impl.is_none() { + panic!("An async sleep implementation is required for retries or timeouts to work. \ + Set the `sleep_impl` on the Config passed into this function to fix this panic."); + } + + let connector = conf.http_connector().and_then(|c| { + let timeout_config = conf + .timeout_config() + .cloned() + .unwrap_or_else(#{TimeoutConfig}::disabled); + let connector_settings = #{ConnectorSettings}::from_timeout_config( + &timeout_config, + ); + c.connector(&connector_settings, conf.sleep_impl()) + }); + + let builder = #{SmithyClientBuilder}::new(); + + let builder = match connector { + // Use provided connector + Some(c) => builder.connector(c), + None =>{ + ##[cfg(feature = "rustls")] + { + // Use default connector based on enabled features + builder.dyn_https_connector(#{ConnectorSettings}::from_timeout_config(&timeout_config)) + } + ##[cfg(not(feature = "rustls"))] + { + panic!("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this."); + } + } + }; + let mut builder = builder + .middleware(#{DynMiddleware}::new(#{Middleware}::new())) + .reconnect_mode(retry_config.reconnect_mode()) + .retry_config(retry_config.into()) + .operation_timeout_config(timeout_config.into()); + builder.set_sleep_impl(sleep_impl); + let client = builder.build(); + + Self { handle: #{Arc}::new(Handle { client, conf }) } + } + """, + *codegenScope, + "Middleware" to codegenContext.runtimeConfig.defaultMiddleware(), + ) + } } } } @@ -243,42 +248,3 @@ private class AwsFluentClientDocs(private val codegenContext: ClientCodegenConte } } } - -private fun renderCustomizableOperationSendMethod( - runtimeConfig: RuntimeConfig, - generics: FluentClientGenerics, - writer: RustWriter, -) { - val operationGenerics = RustGenerics(GenericTypeArg("O"), GenericTypeArg("Retry")) - val handleGenerics = generics.toRustGenerics() - val combinedGenerics = operationGenerics + handleGenerics - - val codegenScope = arrayOf( - "combined_generics_decl" to combinedGenerics.declaration(), - "handle_generics_bounds" to handleGenerics.bounds(), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), - "SdkError" to RuntimeType.sdkError(runtimeConfig), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "ParseHttpResponse" to RuntimeType.parseHttpResponse(runtimeConfig), - ) - - writer.rustTemplate( - """ - impl#{combined_generics_decl:W} CustomizableOperation#{combined_generics_decl:W} - where - #{handle_generics_bounds:W} - { - /// Sends this operation's request - pub async fn send(self) -> Result> - where - E: std::error::Error + Send + Sync + 'static, - O: #{ParseHttpResponse}> + Send + Sync + Clone + 'static, - Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + Send + Sync + Clone, - { - self.handle.client.call(self.operation).await - } - } - """, - *codegenScope, - ) -} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 20d280ddf9..3957f77391 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -17,10 +17,16 @@ import software.amazon.smithy.model.traits.HttpQueryTrait import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.RequestSerializerGenerator +import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientHttpBoundProtocolPayloadGenerator +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs @@ -30,11 +36,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.util.cloneOperation import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -100,12 +104,18 @@ class AwsPresigningDecorator internal constructor( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations + listOf(AwsInputPresignedMethod(codegenContext, operation)) + ): List { + return if (codegenContext.smithyRuntimeMode.generateMiddleware) { + baseCustomizations + AwsInputPresignedMethod(codegenContext, operation) + } else { + baseCustomizations + } + } /** * Adds presignable trait to known presignable operations and creates synthetic presignable shapes for codegen */ - override fun transformModel(service: ServiceShape, model: Model): Model { + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model { val modelWithSynthetics = addSyntheticOperations(model) val presignableTransforms = mutableListOf() val intermediate = ModelTransformer.create().mapShapes(modelWithSynthetics) { shape -> @@ -132,6 +142,7 @@ class AwsPresigningDecorator internal constructor( } } +// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up middleware class AwsInputPresignedMethod( private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, @@ -141,8 +152,8 @@ class AwsInputPresignedMethod( private val codegenScope = ( presigningTypes + listOf( - "PresignedRequestService" to AwsRuntimeType.presigning() - .resolve("service::PresignedRequestService"), + "PresignedRequestService" to AwsRuntimeType.presigningService() + .resolve("PresignedRequestService"), "SdkError" to RuntimeType.sdkError(runtimeConfig), "aws_sigv4" to AwsRuntimeType.awsSigv4(runtimeConfig), "sig_auth" to AwsRuntimeType.awsSigAuth(runtimeConfig), @@ -173,7 +184,7 @@ class AwsInputPresignedMethod( MakeOperationGenerator( codegenContext, protocol, - HttpBoundProtocolPayloadGenerator(codegenContext, protocol), + ClientHttpBoundProtocolPayloadGenerator(codegenContext, protocol), // Prefixed with underscore to avoid colliding with modeled functions functionName = makeOperationFn, public = false, @@ -202,12 +213,16 @@ class AwsInputPresignedMethod( *codegenScope, ) rustBlock("") { - rust( + rustTemplate( """ // Change signature type to query params and wire up presigning config let mut props = request.properties_mut(); - props.insert(presigning_config.start_time()); + props.insert(#{SharedTimeSource}::new(#{StaticTimeSource}::new(presigning_config.start_time()))); """, + "SharedTimeSource" to RuntimeType.smithyAsync(runtimeConfig) + .resolve("time::SharedTimeSource"), + "StaticTimeSource" to RuntimeType.smithyAsync(runtimeConfig) + .resolve("time::StaticTimeSource"), ) withBlock("props.insert(", ");") { rustTemplate( @@ -221,7 +236,7 @@ class AwsInputPresignedMethod( } rustTemplate( """ - let mut config = props.get_mut::<#{sig_auth}::signer::OperationSigningConfig>() + let config = props.get_mut::<#{sig_auth}::signer::OperationSigningConfig>() .expect("signing config added by make_operation()"); config.signature_type = #{sig_auth}::signer::HttpSignatureType::HttpRequestQueryParams; config.expires_in = Some(presigning_config.expires()); @@ -246,10 +261,12 @@ class AwsInputPresignedMethod( } class AwsPresignedFluentBuilderMethod( - runtimeConfig: RuntimeConfig, + private val codegenContext: ClientCodegenContext, ) : FluentClientCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = ( presigningTypes + arrayOf( + *RuntimeType.preludeScope, "Error" to AwsRuntimeType.presigning().resolve("config::Error"), "SdkError" to RuntimeType.sdkError(runtimeConfig), ) @@ -261,20 +278,137 @@ class AwsPresignedFluentBuilderMethod( documentPresignedMethod(hasConfigArg = false) rustBlockTemplate( """ + ##[allow(unused_mut)] pub async fn presigned( - self, + mut self, presigning_config: #{PresigningConfig}, - ) -> Result<#{PresignedRequest}, #{SdkError}<#{OpError}>> + ) -> #{Result}<#{PresignedRequest}, #{SdkError}<#{OpError}, #{RawResponseType}>> """, *codegenScope, "OpError" to section.operationErrorType, + "RawResponseType" to if (codegenContext.smithyRuntimeMode.generateMiddleware) { + RuntimeType.smithyHttp(runtimeConfig).resolve("operation::Response") + } else { + RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse") + }, ) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + renderPresignedMethodBodyMiddleware() + } else { + renderPresignedMethodBody(section) + } + } + } + } + + private fun RustWriter.renderPresignedMethodBodyMiddleware() { + rustTemplate( + """ + let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; + input.presigned(&self.handle.conf, presigning_config).await + """, + *codegenScope, + ) + } + + private fun RustWriter.renderPresignedMethodBody(section: FluentClientSection.FluentBuilderImpl) { + val presignableOp = PRESIGNABLE_OPERATIONS.getValue(section.operationShape.id) + val operationShape = if (presignableOp.hasModelTransforms()) { + codegenContext.model.expectShape(syntheticShapeId(section.operationShape.id), OperationShape::class.java) + } else { + section.operationShape + } + + rustTemplate( + """ + #{alternate_presigning_serializer} + + let runtime_plugins = #{Operation}::operation_runtime_plugins( + self.handle.runtime_plugins.clone(), + &self.handle.conf, + self.config_override, + ) + .with_client_plugin(#{SigV4PresigningRuntimePlugin}::new(presigning_config, #{payload_override})) + #{alternate_presigning_serializer_registration}; + + let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; + let mut context = #{Operation}::orchestrate_with_stop_point(&runtime_plugins, input, #{StopPoint}::BeforeTransmit) + .await + .map_err(|err| { + err.map_service_error(|err| { + err.downcast::<#{OperationError}>().expect("correct error type") + }) + })?; + let request = context.take_request().expect("request set before transmit"); + Ok(#{PresignedRequest}::new(request.map(|_| ()))) + """, + *codegenScope, + "Operation" to codegenContext.symbolProvider.toSymbol(section.operationShape), + "OperationError" to section.operationErrorType, + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors") + .resolve("SharedInterceptor"), + "SigV4PresigningRuntimePlugin" to AwsRuntimeType.presigningInterceptor(runtimeConfig) + .resolve("SigV4PresigningRuntimePlugin"), + "StopPoint" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::StopPoint"), + "USER_AGENT" to CargoDependency.Http.toType().resolve("header::USER_AGENT"), + "alternate_presigning_serializer" to writable { + if (presignableOp.hasModelTransforms()) { + val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) rustTemplate( """ - let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; - input.presigned(&self.handle.conf, presigning_config).await + ##[derive(::std::fmt::Debug)] + struct AlternatePresigningSerializerRuntimePlugin; + impl #{RuntimePlugin} for AlternatePresigningSerializerRuntimePlugin { + fn config(&self) -> #{Option}<#{FrozenLayer}> { + let mut cfg = #{Layer}::new("presigning_serializer"); + cfg.store_put(#{SharedRequestSerializer}::new(#{AlternateSerializer})); + #{Some}(cfg.freeze()) + } + } """, - *codegenScope, + *preludeScope, + "AlternateSerializer" to alternateSerializer(operationShape), + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), + "SharedRequestSerializer" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) + .resolve("client::ser_de::SharedRequestSerializer"), + ) + } + }, + "alternate_presigning_serializer_registration" to writable { + if (presignableOp.hasModelTransforms()) { + rust(".with_operation_plugin(AlternatePresigningSerializerRuntimePlugin)") + } + }, + "payload_override" to writable { + rustTemplate( + "#{aws_sigv4}::http_request::SignableBody::" + + when (presignableOp.payloadSigningType) { + PayloadSigningType.EMPTY -> "Bytes(b\"\")" + PayloadSigningType.UNSIGNED_PAYLOAD -> "UnsignedPayload" + }, + "aws_sigv4" to AwsRuntimeType.awsSigv4(runtimeConfig), + ) + }, + ) + } + + private fun alternateSerializer(transformedOperationShape: OperationShape): RuntimeType = + transformedOperationShape.contextName(codegenContext.serviceShape).replaceFirstChar { + it.uppercase() + }.let { baseName -> + "${baseName}PresigningRequestSerializer".let { name -> + RuntimeType.forInlineFun(name, codegenContext.symbolProvider.moduleForShape(transformedOperationShape)) { + RequestSerializerGenerator( + codegenContext, + codegenContext.protocolImpl!!, + null, + nameOverride = name, + ).render( + this, + transformedOperationShape, ) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt index 60bf14f9ea..ec7e0ba3d0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt @@ -43,7 +43,21 @@ fun RuntimeConfig.awsRoot(): RuntimeCrateLocation { object AwsRuntimeType { fun presigning(): RuntimeType = RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) + fun presigningInterceptor(runtimeConfig: RuntimeConfig): RuntimeType = + RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "presigning_interceptors", + visibility = Visibility.PUBCRATE, + AwsCargoDependency.awsSigv4(runtimeConfig), + CargoDependency.smithyRuntimeApi(runtimeConfig), + ), + ) + // TODO(enableNewSmithyRuntimeCleanup): Delete the `presigning_service.rs` inlineable when cleaning up middleware + fun presigningService(): RuntimeType = + RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning_service", visibility = Visibility.PUBCRATE)) + + // TODO(enableNewSmithyRuntimeCleanup): Delete defaultMiddleware and middleware.rs, and remove tower dependency from inlinables, when cleaning up middleware fun RuntimeConfig.defaultMiddleware() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( "middleware", visibility = Visibility.PUBLIC, @@ -70,4 +84,7 @@ object AwsRuntimeType { fun awsSigv4(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsSigv4(runtimeConfig).toType() fun awsTypes(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsTypes(runtimeConfig).toType() + + fun awsRuntime(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsRuntime(runtimeConfig).toType() + fun awsRuntimeApi(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsRuntimeApi(runtimeConfig).toType() } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index e1f08ebb05..9b8adeddab 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -9,6 +9,8 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -18,8 +20,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index 5aaaeab950..c25001cb30 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -8,6 +8,8 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -15,9 +17,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization class CredentialsCacheDecorator : ClientCodegenDecorator { @@ -27,7 +29,7 @@ class CredentialsCacheDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { - return baseCustomizations + CredentialCacheConfig(codegenContext.runtimeConfig) + return baseCustomizations + CredentialCacheConfig(codegenContext) } override fun operationCustomizations( @@ -49,75 +51,171 @@ class CredentialsCacheDecorator : ClientCodegenDecorator { /** * Add a `.credentials_cache` field and builder to the `Config` for a given service */ -class CredentialCacheConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization() { +class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val codegenScope = arrayOf( - "cache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache"), - "provider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider"), + *preludeScope, + "CredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache::CredentialsCache"), "DefaultProvider" to defaultProvider(), + "SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"), + "SharedCredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache::SharedCredentialsCache"), + "SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider::SharedCredentialsProvider"), ) override fun section(section: ServiceConfig) = writable { when (section) { - ServiceConfig.ConfigStruct -> rustTemplate( - """pub(crate) credentials_cache: #{cache}::SharedCredentialsCache,""", - *codegenScope, - ) - - ServiceConfig.ConfigImpl -> rustTemplate( - """ - /// Returns the credentials cache. - pub fn credentials_cache(&self) -> #{cache}::SharedCredentialsCache { - self.credentials_cache.clone() + ServiceConfig.ConfigStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate( + """pub(crate) credentials_cache: #{SharedCredentialsCache},""", + *codegenScope, + ) } - """, - *codegenScope, - ) + } + + ServiceConfig.ConfigImpl -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Returns the credentials cache. + pub fn credentials_cache(&self) -> #{Option}<#{SharedCredentialsCache}> { + self.config.load::<#{SharedCredentialsCache}>().cloned() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns the credentials cache. + pub fn credentials_cache(&self) -> #{SharedCredentialsCache} { + self.credentials_cache.clone() + } + """, + *codegenScope, + ) + } + } ServiceConfig.BuilderStruct -> - rustTemplate("credentials_cache: Option<#{cache}::CredentialsCache>,", *codegenScope) + if (runtimeMode.generateMiddleware) { + rustTemplate("credentials_cache: #{Option}<#{CredentialsCache}>,", *codegenScope) + } ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Sets the credentials cache for this service - pub fn credentials_cache(mut self, credentials_cache: #{cache}::CredentialsCache) -> Self { - self.set_credentials_cache(Some(credentials_cache)); + pub fn credentials_cache(mut self, credentials_cache: #{CredentialsCache}) -> Self { + self.set_credentials_cache(#{Some}(credentials_cache)); self } - /// Sets the credentials cache for this service - pub fn set_credentials_cache(&mut self, credentials_cache: Option<#{cache}::CredentialsCache>) -> &mut Self { - self.credentials_cache = credentials_cache; - self - } """, *codegenScope, ) + + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Sets the credentials cache for this service + pub fn set_credentials_cache(&mut self, credentials_cache: #{Option}<#{CredentialsCache}>) -> &mut Self { + self.config.store_or_unset(credentials_cache); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the credentials cache for this service + pub fn set_credentials_cache(&mut self, credentials_cache: Option<#{CredentialsCache}>) -> &mut Self { + self.credentials_cache = credentials_cache; + self + } + """, + *codegenScope, + ) + } } - ServiceConfig.BuilderBuild -> rustTemplate( - """ - credentials_cache: self - .credentials_cache - .unwrap_or_else({ - let sleep = self.sleep_impl.clone(); - || match sleep { - Some(sleep) => { - #{cache}::CredentialsCache::lazy_builder() - .sleep(sleep) - .into_credentials_cache() - } - None => #{cache}::CredentialsCache::lazy(), + ServiceConfig.BuilderBuild -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + if let Some(credentials_provider) = layer.load::<#{SharedCredentialsProvider}>().cloned() { + let cache_config = layer.load::<#{CredentialsCache}>().cloned() + .unwrap_or_else({ + let sleep = self.runtime_components.sleep_impl(); + || match sleep { + Some(sleep) => { + #{CredentialsCache}::lazy_builder() + .sleep(sleep) + .into_credentials_cache() + } + None => #{CredentialsCache}::lazy(), + } + }); + let shared_credentials_cache = cache_config.create_cache(credentials_provider); + layer.store_put(shared_credentials_cache); } - }) - .create_cache( - self.credentials_provider.unwrap_or_else(|| { - #{provider}::SharedCredentialsProvider::new(#{DefaultProvider}) - }) - ), - """, - *codegenScope, - ) + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + credentials_cache: self + .credentials_cache + .unwrap_or_else({ + let sleep = self.sleep_impl.clone(); + || match sleep { + Some(sleep) => { + #{CredentialsCache}::lazy_builder() + .sleep(sleep) + .into_credentials_cache() + } + None => #{CredentialsCache}::lazy(), + } + }) + .create_cache( + self.credentials_provider.unwrap_or_else(|| { + #{SharedCredentialsProvider}::new(#{DefaultProvider}) + }) + ), + """, + *codegenScope, + ) + } + } + + is ServiceConfig.OperationConfigOverride -> { + rustTemplate( + """ + match ( + resolver.config_mut().load::<#{CredentialsCache}>().cloned(), + resolver.config_mut().load::<#{SharedCredentialsProvider}>().cloned(), + ) { + (#{None}, #{None}) => {} + (#{None}, _) => { + panic!("also specify `.credentials_cache` when overriding credentials provider for the operation"); + } + (_, #{None}) => { + panic!("also specify `.credentials_provider` when overriding credentials cache for the operation"); + } + ( + #{Some}(credentials_cache), + #{Some}(credentials_provider), + ) => { + resolver.config_mut().store_put(credentials_cache.create_cache(credentials_provider)); + } + } + """, + *codegenScope, + ) + } else -> emptySection } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 8b2e65a4b3..0437075d3e 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -9,39 +9,52 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization +import software.amazon.smithy.rust.codegen.core.util.letIf class CredentialsProviderDecorator : ClientCodegenDecorator { override val name: String = "CredentialsProvider" override val order: Byte = 0 + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(CredentialsIdentityResolverRegistration(codegenContext)) + } + override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { - return baseCustomizations + CredentialProviderConfig(codegenContext.runtimeConfig) + return baseCustomizations + CredentialProviderConfig(codegenContext) } override fun extraSections(codegenContext: ClientCodegenContext): List = listOf( adhocCustomization { section -> - rust("${section.serviceConfigBuilder}.set_credentials_provider(${section.sdkConfig}.credentials_provider().cloned());") + rust("${section.serviceConfigBuilder}.set_credentials_provider(${section.sdkConfig}.credentials_provider());") }, ) override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { rustCrate.mergeFeature(TestUtilFeature.copy(deps = listOf("aws-credential-types/test-util"))) - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rust( "pub use #T::Credentials;", AwsRuntimeType.awsCredentialTypes(codegenContext.runtimeConfig), @@ -53,38 +66,63 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { /** * Add a `.credentials_provider` field and builder to the `Config` for a given service */ -class CredentialProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization() { +class CredentialProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val smithyRuntimeMode = codegenContext.smithyRuntimeMode + private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = arrayOf( - "provider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider"), + *preludeScope, "Credentials" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("Credentials"), + "ProvideCredentials" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider::ProvideCredentials"), + "SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider::SharedCredentialsProvider"), "TestCredentials" to AwsRuntimeType.awsCredentialTypesTestUtil(runtimeConfig).resolve("Credentials"), ) override fun section(section: ServiceConfig) = writable { when (section) { - ServiceConfig.BuilderStruct -> - rustTemplate("credentials_provider: Option<#{provider}::SharedCredentialsProvider>,", *codegenScope) + ServiceConfig.BuilderStruct -> { + if (smithyRuntimeMode.generateMiddleware) { + rustTemplate("credentials_provider: #{Option}<#{SharedCredentialsProvider}>,", *codegenScope) + } + } ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Sets the credentials provider for this service - pub fn credentials_provider(mut self, credentials_provider: impl #{provider}::ProvideCredentials + 'static) -> Self { - self.set_credentials_provider(Some(#{provider}::SharedCredentialsProvider::new(credentials_provider))); - self - } - - /// Sets the credentials provider for this service - pub fn set_credentials_provider(&mut self, credentials_provider: Option<#{provider}::SharedCredentialsProvider>) -> &mut Self { - self.credentials_provider = credentials_provider; + pub fn credentials_provider(mut self, credentials_provider: impl #{ProvideCredentials} + 'static) -> Self { + self.set_credentials_provider(#{Some}(#{SharedCredentialsProvider}::new(credentials_provider))); self } """, *codegenScope, ) + + if (smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Sets the credentials provider for this service + pub fn set_credentials_provider(&mut self, credentials_provider: #{Option}<#{SharedCredentialsProvider}>) -> &mut Self { + self.config.store_or_unset(credentials_provider); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the credentials provider for this service + pub fn set_credentials_provider(&mut self, credentials_provider: #{Option}<#{SharedCredentialsProvider}>) -> &mut Self { + self.credentials_provider = credentials_provider; + self + } + """, + *codegenScope, + ) + } } is ServiceConfig.DefaultForTests -> rustTemplate( - "${section.configBuilderRef}.set_credentials_provider(Some(#{provider}::SharedCredentialsProvider::new(#{TestCredentials}::for_tests())));", + "${section.configBuilderRef}.set_credentials_provider(Some(#{SharedCredentialsProvider}::new(#{TestCredentials}::for_tests())));", *codegenScope, ) @@ -93,5 +131,37 @@ class CredentialProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomizati } } +class CredentialsIdentityResolverRegistration( + codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { + rustBlockTemplate("if let Some(credentials_cache) = ${section.serviceConfigName}.credentials_cache()") { + section.registerIdentityResolver(this) { + rustTemplate( + """ + #{SIGV4_SCHEME_ID}, + #{SharedIdentityResolver}::new( + #{CredentialsIdentityResolver}::new(credentials_cache), + ), + """, + "SIGV4_SCHEME_ID" to AwsRuntimeType.awsRuntime(runtimeConfig) + .resolve("auth::sigv4::SCHEME_ID"), + "CredentialsIdentityResolver" to AwsRuntimeType.awsRuntime(runtimeConfig) + .resolve("identity::credentials::CredentialsIdentityResolver"), + "SharedIdentityResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::identity::SharedIdentityResolver"), + ) + } + } + } + else -> {} + } + } +} + fun defaultProvider() = RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("no_credentials")).resolve("NoCredentials") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt index 1ac83c10ca..e59eaa7bc8 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt @@ -20,20 +20,28 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointRulesetIndex import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.symbol import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.loadFromConfigBag import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam +import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization +import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.extendIf import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import java.util.Optional /** load a builtIn parameter from a ruleset by name */ @@ -48,14 +56,25 @@ fun ClientCodegenContext.getBuiltIn(builtIn: String): Parameter? { return rules.getBuiltIn(builtIn) } -private fun toConfigParam(parameter: Parameter): ConfigParam = ConfigParam( - parameter.name.rustName(), - when (parameter.type!!) { - ParameterType.STRING -> RuntimeType.String.toSymbol() - ParameterType.BOOLEAN -> RuntimeType.Bool.toSymbol() - }, - parameter.documentation.orNull()?.let { writable { docs(it) } }, -) +private fun promotedBuiltins(parameter: Parameter) = + parameter == Builtins.FIPS || parameter == Builtins.DUALSTACK || parameter == Builtins.SDK_ENDPOINT + +private fun configParamNewtype(parameter: Parameter, name: String, runtimeConfig: RuntimeConfig): RuntimeType { + val type = parameter.symbol().mapRustType { t -> t.stripOuter() } + return when (promotedBuiltins(parameter)) { + true -> AwsRuntimeType.awsTypes(runtimeConfig) + .resolve("endpoint_config::${name.toPascalCase()}") + + false -> configParamNewtype(name.toPascalCase(), type, runtimeConfig) + } +} + +private fun ConfigParam.Builder.toConfigParam(parameter: Parameter, runtimeConfig: RuntimeConfig): ConfigParam = + this.name(this.name ?: parameter.name.rustName()) + .type(parameter.symbol().mapRustType { t -> t.stripOuter() }) + .newtype(configParamNewtype(parameter, this.name!!, runtimeConfig)) + .setterDocs(this.setterDocs ?: parameter.documentation.orNull()?.let { writable { docs(it) } }) + .build() fun Model.loadBuiltIn(serviceId: ShapeId, builtInSrc: Parameter): Parameter? { val model = this @@ -82,14 +101,14 @@ fun Model.sdkConfigSetter( } /** - * Create a client codegen decorator that creates bindings for a builtIn parameter. Optionally, you can provide [clientParam] - * which allows control over the config parameter that will be generated. + * Create a client codegen decorator that creates bindings for a builtIn parameter. Optionally, you can provide + * [clientParam.Builder] which allows control over the config parameter that will be generated. */ fun decoratorForBuiltIn( builtIn: Parameter, - clientParam: ConfigParam? = null, + clientParamBuilder: ConfigParam.Builder? = null, ): ClientCodegenDecorator { - val nameOverride = clientParam?.name + val nameOverride = clientParamBuilder?.name val name = nameOverride ?: builtIn.name.rustName() return object : ClientCodegenDecorator { override val name: String = "Auto${builtIn.builtIn.get()}" @@ -100,7 +119,7 @@ fun decoratorForBuiltIn( override fun extraSections(codegenContext: ClientCodegenContext): List { return listOfNotNull( - codegenContext.model.sdkConfigSetter(codegenContext.serviceShape.id, builtIn, clientParam?.name), + codegenContext.model.sdkConfigSetter(codegenContext.serviceShape.id, builtIn, clientParamBuilder?.name), ) } @@ -110,7 +129,9 @@ fun decoratorForBuiltIn( ): List { return baseCustomizations.extendIf(rulesetContainsBuiltIn(codegenContext)) { standardConfigParam( - clientParam ?: toConfigParam(builtIn), + clientParamBuilder?.toConfigParam(builtIn, codegenContext.runtimeConfig) ?: ConfigParam.Builder() + .toConfigParam(builtIn, codegenContext.runtimeConfig), + codegenContext, ) } } @@ -120,11 +141,21 @@ fun decoratorForBuiltIn( override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? = when (parameter.builtIn) { builtIn.builtIn -> writable { - rust("$configRef.$name") - if (parameter.type == ParameterType.STRING) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + val newtype = configParamNewtype(parameter, name, codegenContext.runtimeConfig) + val symbol = parameter.symbol().mapRustType { t -> t.stripOuter() } + rustTemplate( + """$configRef.#{load_from_service_config_layer}""", + "load_from_service_config_layer" to loadFromConfigBag(symbol.name, newtype), + ) + } else { + rust("$configRef.$name") + } + if (codegenContext.smithyRuntimeMode.generateMiddleware && parameter.type == ParameterType.STRING) { rust(".clone()") } } + else -> null } @@ -147,7 +178,7 @@ fun decoratorForBuiltIn( private val endpointUrlDocs = writable { rust( """ - /// Sets the endpoint url used to communicate with this service + /// Sets the endpoint URL used to communicate with this service /// Note: this is used in combination with other endpoint rules, e.g. an API that applies a host-label prefix /// will be prefixed onto this URL. To fully override the endpoint resolver, use @@ -173,6 +204,6 @@ val PromotedBuiltInsDecorators = decoratorForBuiltIn(Builtins.DUALSTACK), decoratorForBuiltIn( Builtins.SDK_ENDPOINT, - ConfigParam("endpoint_url", RuntimeType.String.toSymbol(), endpointUrlDocs), + ConfigParam.Builder().name("endpoint_url").type(RuntimeType.String.toSymbol()).setterDocs(endpointUrlDocs), ), ).toTypedArray() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt index fcf3cc0c2b..d2d653e1b5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -13,9 +13,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.util.letIf +// TODO(enableNewSmithyRuntimeCleanup): Delete this decorator since it's now in `codegen-client` class HttpConnectorDecorator : ClientCodegenDecorator { override val name: String = "HttpConnectorDecorator" override val order: Byte = 0 @@ -23,38 +25,57 @@ class HttpConnectorDecorator : ClientCodegenDecorator { override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, - ): List { - return baseCustomizations + HttpConnectorConfigCustomization(codegenContext) - } + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateMiddleware) { + it + HttpConnectorConfigCustomization(codegenContext) + } } class HttpConnectorConfigCustomization( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, ) : ConfigCustomization() { private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( + *preludeScope, "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), ) override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { - rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + if (runtimeMode.generateMiddleware) { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } } is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. - pub fn http_connector(&self) -> Option<&#{HttpConnector}> { - self.http_connector.as_ref() - } - """, - *codegenScope, - ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.config.load::<#{HttpConnector}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.http_connector.as_ref() + } + """, + *codegenScope, + ) + } } is ServiceConfig.BuilderStruct -> writable { - rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + if (runtimeMode.generateMiddleware) { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } } ServiceConfig.BuilderImpl -> writable { rustTemplate( @@ -91,7 +112,7 @@ class HttpConnectorConfigCustomization( /// ## } /// ``` pub fn http_connector(mut self, http_connector: impl Into<#{HttpConnector}>) -> Self { - self.http_connector = Some(http_connector.into()); + self.set_http_connector(#{Some}(http_connector)); self } @@ -133,16 +154,35 @@ class HttpConnectorConfigCustomization( /// ## } /// ## } /// ``` - pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { - self.http_connector = http_connector.map(|inner| inner.into()); - self - } """, *codegenScope, ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + pub fn set_http_connector(&mut self, http_connector: #{Option}>) -> &mut Self { + http_connector.map(|c| self.config.store_put(c.into())); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + pub fn set_http_connector(&mut self, http_connector: #{Option}>) -> &mut Self { + self.http_connector = http_connector.map(|inner| inner.into()); + self + } + """, + *codegenScope, + ) + } } is ServiceConfig.BuilderBuild -> writable { - rust("http_connector: self.http_connector,") + if (runtimeMode.generateMiddleware) { + rust("http_connector: self.http_connector,") + } } else -> emptySection } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index f2913a1aa7..610ad1c579 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -9,25 +9,41 @@ import software.amazon.smithy.aws.traits.HttpChecksumTrait import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape -import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.orNull -fun RuntimeConfig.awsInlineableBodyWithChecksum() = RuntimeType.forInlineDependency( +private fun RuntimeConfig.awsInlineableHttpRequestChecksum() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( - "http_body_checksum", visibility = Visibility.PUBCRATE, + "http_request_checksum", visibility = Visibility.PUBCRATE, + CargoDependency.Bytes, + CargoDependency.Http, + CargoDependency.HttpBody, + CargoDependency.Tracing, + CargoDependency.smithyChecksums(this), + CargoDependency.smithyHttp(this), + CargoDependency.smithyRuntimeApi(this), + CargoDependency.smithyTypes(this), + ), +) + +// TODO(enableNewSmithyRuntimeCleanup): Delete this method +fun RuntimeConfig.awsInlineableBodyWithChecksumMiddleware() = RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "http_body_checksum_middleware", visibility = Visibility.PUBCRATE, CargoDependency.Http, CargoDependency.HttpBody, CargoDependency.smithyHttp(this), @@ -42,17 +58,12 @@ class HttpRequestChecksumDecorator : ClientCodegenDecorator { override val name: String = "HttpRequestChecksum" override val order: Byte = 0 - // TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator - private fun applies(codegenContext: ClientCodegenContext): Boolean = - !codegenContext.settings.codegenConfig.enableNewSmithyRuntime - override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations.letIf(applies(codegenContext)) { - it + HttpRequestChecksumCustomization(codegenContext, operation) - } + ): List = + baseCustomizations + HttpRequestChecksumCustomization(codegenContext, operation) } private fun HttpChecksumTrait.requestAlgorithmMember( @@ -69,6 +80,8 @@ private fun HttpChecksumTrait.requestAlgorithmMember( private fun HttpChecksumTrait.checksumAlgorithmToStr( codegenContext: ClientCodegenContext, operationShape: OperationShape, + // TODO(enableNewSmithyRuntimeCleanup): Remove `algorithmWasCloned` (it should always be false) + algorithmWasCloned: Boolean, ): Writable { val runtimeConfig = codegenContext.runtimeConfig val requestAlgorithmMember = this.requestAlgorithmMember(codegenContext, operationShape) @@ -76,8 +89,10 @@ private fun HttpChecksumTrait.checksumAlgorithmToStr( return { if (requestAlgorithmMember != null) { - // User may set checksum for requests, and we need to call as_ref before we can convert the algorithm to a &str - rust("let checksum_algorithm = $requestAlgorithmMember.as_ref();") + if (algorithmWasCloned) { + // User may set checksum for requests, and we need to call as_ref before we can convert the algorithm to a &str + rust("let checksum_algorithm = $requestAlgorithmMember.as_ref();") + } if (isRequestChecksumRequired) { // Checksums are required, fall back to MD5 @@ -118,54 +133,76 @@ class HttpRequestChecksumCustomization( ) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig - override fun section(section: OperationSection): Writable { + override fun section(section: OperationSection): Writable = writable { // Get the `HttpChecksumTrait`, returning early if this `OperationShape` doesn't have one - val checksumTrait = operationShape.getTrait() ?: return emptySection - val checksumAlgorithm = checksumTrait.requestAlgorithmMember(codegenContext, operationShape) - - return when (section) { - is OperationSection.MutateInput -> { - // Various other things will consume the input struct before we can get at the checksum algorithm - // field within it. This ensures that we preserve a copy of it. It's an enum so cloning is cheap. - if (checksumAlgorithm != null) { - return { - rust("let $checksumAlgorithm = self.$checksumAlgorithm().cloned();") - } - } else { - emptySection - } - } - is OperationSection.MutateRequest -> { - // Return early if no request checksum can be set nor is it required - if (!checksumTrait.isRequestChecksumRequired && checksumAlgorithm == null) { - return emptySection - } else { - // `add_checksum_calculation_to_request` handles both streaming and in-memory request bodies. - return { + val checksumTrait = operationShape.getTrait() ?: return@writable + val requestAlgorithmMember = checksumTrait.requestAlgorithmMember(codegenContext, operationShape) + val inputShape = codegenContext.model.expectShape(operationShape.inputShape) + + when (section) { + is OperationSection.AdditionalInterceptors -> { + if (requestAlgorithmMember != null) { + section.registerInterceptor(runtimeConfig, this) { + val runtimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) rustTemplate( """ - ${section.request} = ${section.request}.augment(|mut req, properties| { - #{checksum_algorithm_to_str:W} - if let Some(checksum_algorithm) = checksum_algorithm { - #{add_checksum_calculation_to_request}(&mut req, properties, checksum_algorithm)?; - } - Result::<_, #{BuildError}>::Ok(req) - })?; + #{RequestChecksumInterceptor}::new(|input: &#{Input}| { + let input: &#{OperationInput} = input.downcast_ref().expect("correct type"); + let checksum_algorithm = input.$requestAlgorithmMember(); + #{checksum_algorithm_to_str} + #{Result}::<_, #{BoxError}>::Ok(checksum_algorithm) + }) """, + *preludeScope, + "BoxError" to RuntimeType.boxError(runtimeConfig), + "Input" to runtimeApi.resolve("client::interceptors::context::Input"), + "OperationInput" to codegenContext.symbolProvider.toSymbol(inputShape), + "RequestChecksumInterceptor" to runtimeConfig.awsInlineableHttpRequestChecksum() + .resolve("RequestChecksumInterceptor"), "checksum_algorithm_to_str" to checksumTrait.checksumAlgorithmToStr( codegenContext, operationShape, + algorithmWasCloned = false, ), - "add_checksum_calculation_to_request" to runtimeConfig.awsInlineableBodyWithChecksum() - .resolve("add_checksum_calculation_to_request"), - "BuildError" to runtimeConfig.operationBuildError(), ) } } } - else -> { - return emptySection + // TODO(enableNewSmithyRuntimeCleanup): Delete `is OperationSection.MutateInput` + is OperationSection.MutateInput -> { + // Various other things will consume the input struct before we can get at the checksum algorithm + // field within it. This ensures that we preserve a copy of it. It's an enum so cloning is cheap. + if (requestAlgorithmMember != null) { + rust("let $requestAlgorithmMember = self.$requestAlgorithmMember().cloned();") + } + } + // TODO(enableNewSmithyRuntimeCleanup): Delete `is OperationSection.MutateRequest` + is OperationSection.MutateRequest -> { + // Return early if no request checksum can be set nor is it required + if (checksumTrait.isRequestChecksumRequired || requestAlgorithmMember != null) { + // `add_checksum_calculation_to_request` handles both streaming and in-memory request bodies. + rustTemplate( + """ + ${section.request} = ${section.request}.augment(|mut req, properties| { + #{checksum_algorithm_to_str:W} + if let Some(checksum_algorithm) = checksum_algorithm { + #{add_checksum_calculation_to_request}(&mut req, properties, checksum_algorithm)?; + } + Result::<_, #{BuildError}>::Ok(req) + })?; + """, + "checksum_algorithm_to_str" to checksumTrait.checksumAlgorithmToStr( + codegenContext, + operationShape, + algorithmWasCloned = true, + ), + "add_checksum_calculation_to_request" to runtimeConfig.awsInlineableBodyWithChecksumMiddleware() + .resolve("add_checksum_calculation_to_request"), + "BuildError" to runtimeConfig.operationBuildError(), + ) + } } + else -> { } } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt index 832ab7e07d..114644daaf 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt @@ -11,16 +11,36 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.orNull +private fun RuntimeConfig.awsInlineableHttpResponseChecksum() = RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "http_response_checksum", visibility = Visibility.PUBCRATE, + CargoDependency.Bytes, + CargoDependency.Http, + CargoDependency.HttpBody, + CargoDependency.Tracing, + CargoDependency.smithyChecksums(this), + CargoDependency.smithyHttp(this), + CargoDependency.smithyRuntimeApi(this), + CargoDependency.smithyTypes(this), + ), +) + fun HttpChecksumTrait.requestValidationModeMember( codegenContext: ClientCodegenContext, operationShape: OperationShape, @@ -33,15 +53,14 @@ class HttpResponseChecksumDecorator : ClientCodegenDecorator { override val name: String = "HttpResponseChecksum" override val order: Byte = 0 - // TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator - private fun applies(codegenContext: ClientCodegenContext, operationShape: OperationShape): Boolean = - !codegenContext.settings.codegenConfig.enableNewSmithyRuntime && operationShape.outputShape != ShapeId.from("com.amazonaws.s3#GetObjectOutput") + private fun applies(operationShape: OperationShape): Boolean = + operationShape.outputShape != ShapeId.from("com.amazonaws.s3#GetObjectOutput") override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations.letIf(applies(codegenContext, operation)) { + ): List = baseCustomizations.letIf(applies(operation)) { it + HttpResponseChecksumCustomization(codegenContext, operation) } } @@ -52,75 +71,110 @@ class HttpResponseChecksumCustomization( private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, ) : OperationCustomization() { - override fun section(section: OperationSection): Writable { - val checksumTrait = operationShape.getTrait() ?: return emptySection + override fun section(section: OperationSection): Writable = writable { + val checksumTrait = operationShape.getTrait() ?: return@writable val requestValidationModeMember = - checksumTrait.requestValidationModeMember(codegenContext, operationShape) ?: return emptySection + checksumTrait.requestValidationModeMember(codegenContext, operationShape) ?: return@writable val requestValidationModeMemberInner = if (requestValidationModeMember.isOptional) { codegenContext.model.expectShape(requestValidationModeMember.target) } else { requestValidationModeMember } val validationModeName = codegenContext.symbolProvider.toMemberName(requestValidationModeMember) + val inputShape = codegenContext.model.expectShape(operationShape.inputShape) when (section) { - is OperationSection.MutateRequest -> { - // Otherwise, we need to set a property that the `MutateOutput` section handler will read to know if it - // should checksum validate the response. - return { + is OperationSection.AdditionalInterceptors -> { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + // CRC32, CRC32C, SHA256, SHA1 -> "crc32", "crc32c", "sha256", "sha1" + val responseAlgorithms = checksumTrait.responseAlgorithms + .map { algorithm -> algorithm.lowercase() }.joinToString(", ") { algorithm -> "\"$algorithm\"" } + val runtimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) rustTemplate( """ - if let Some($validationModeName) = self.$validationModeName.as_ref() { - let $validationModeName = $validationModeName.clone(); - // Place #{ValidationModeShape} in the property bag so we can check - // it during response deserialization to see if we need to checksum validate - // the response body. - let _ = request.properties_mut().insert($validationModeName); - } + #{ResponseChecksumInterceptor}::new( + [$responseAlgorithms].as_slice(), + |input: &#{Input}| { + ${""/* + Per [the spec](https://smithy.io/2.0/aws/aws-core.html#http-response-checksums), + we check to see if it's the `ENABLED` variant + */} + let input: &#{OperationInput} = input.downcast_ref().expect("correct type"); + matches!(input.$validationModeName(), #{Some}(#{ValidationModeShape}::Enabled)) + } + ) """, + *preludeScope, + "ResponseChecksumInterceptor" to codegenContext.runtimeConfig.awsInlineableHttpResponseChecksum() + .resolve("ResponseChecksumInterceptor"), + "Input" to runtimeApi.resolve("client::interceptors::context::Input"), + "OperationInput" to codegenContext.symbolProvider.toSymbol(inputShape), "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), ) } } + // TODO(enableNewSmithyRuntimeCleanup): Delete `is OperationSection.MutateRequest` + is OperationSection.MutateRequest -> { + // Otherwise, we need to set a property that the `MutateOutput` section handler will read to know if it + // should checksum validate the response. + rustTemplate( + """ + if let Some($validationModeName) = self.$validationModeName.as_ref() { + let $validationModeName = $validationModeName.clone(); + // Place #{ValidationModeShape} in the property bag so we can check + // it during response deserialization to see if we need to checksum validate + // the response body. + let _ = request.properties_mut().insert($validationModeName); + } + """, + "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), + ) + } + // TODO(enableNewSmithyRuntimeCleanup): Delete `is OperationSection.MutateOutput` is OperationSection.MutateOutput -> { + if (!section.propertyBagAvailable) { + return@writable + } + // CRC32, CRC32C, SHA256, SHA1 -> "crc32", "crc32c", "sha256", "sha1" val responseAlgorithms = checksumTrait.responseAlgorithms .map { algorithm -> algorithm.lowercase() }.joinToString(", ") { algorithm -> "\"$algorithm\"" } - return { - rustTemplate( - """ - let response_algorithms = [$responseAlgorithms].as_slice(); - let $validationModeName = properties.get::<#{ValidationModeShape}>(); - // Per [the spec](https://awslabs.github.io/smithy/1.0/spec/aws/aws-core.html##http-response-checksums), - // we check to see if it's the `ENABLED` variant - if matches!($validationModeName, Some(&#{ValidationModeShape}::Enabled)) { - if let Some((checksum_algorithm, precalculated_checksum)) = - #{check_headers_for_precalculated_checksum}( - response.headers(), - response_algorithms, - ) - { - let bytestream = output.body.take().map(|bytestream| { - bytestream.map(move |sdk_body| { - #{wrap_body_with_checksum_validator}( - sdk_body, - checksum_algorithm, - precalculated_checksum.clone(), - ) - }) - }); - output = output.set_body(bytestream); - } + rustTemplate( + """ + let response_algorithms = [$responseAlgorithms].as_slice(); + let $validationModeName = properties.get::<#{ValidationModeShape}>(); + // Per [the spec](https://smithy.io/2.0/aws/aws-core.html##http-response-checksums), + // we check to see if it's the `ENABLED` variant + if matches!($validationModeName, Some(&#{ValidationModeShape}::Enabled)) { + if let Some((checksum_algorithm, precalculated_checksum)) = + #{check_headers_for_precalculated_checksum}( + response.headers(), + response_algorithms, + ) + { + let bytestream = output.body.take().map(|bytestream| { + bytestream.map(move |sdk_body| { + #{wrap_body_with_checksum_validator}( + sdk_body, + checksum_algorithm, + precalculated_checksum.clone(), + ) + }) + }); + output = output.set_body(bytestream); } - """, - "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), - "wrap_body_with_checksum_validator" to codegenContext.runtimeConfig.awsInlineableBodyWithChecksum().resolve("wrap_body_with_checksum_validator"), - "check_headers_for_precalculated_checksum" to codegenContext.runtimeConfig.awsInlineableBodyWithChecksum().resolve("check_headers_for_precalculated_checksum"), - ) - } + } + """, + "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), + "wrap_body_with_checksum_validator" to codegenContext.runtimeConfig.awsInlineableBodyWithChecksumMiddleware() + .resolve("wrap_body_with_checksum_validator"), + "check_headers_for_precalculated_checksum" to codegenContext.runtimeConfig.awsInlineableBodyWithChecksumMiddleware() + .resolve("check_headers_for_precalculated_checksum"), + ) } - else -> return emptySection + + else -> {} } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 6eedc44a89..3cee565e64 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.Approx import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.AsyncStd import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.AsyncStream import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.BytesUtils @@ -17,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Compani import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.FuturesUtil import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.HdrHistogram import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.Hound +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.HttpBody import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.SerdeJson import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.Smol import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.TempFile @@ -25,12 +27,15 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Compani import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.TracingAppender import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.TracingSubscriber import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.TracingTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.smithyRuntime +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.smithyRuntimeApi import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.testutil.testDependenciesOnly +import software.amazon.smithy.rustsdk.AwsCargoDependency.awsRuntime import java.nio.file.Files import java.nio.file.Paths import kotlin.io.path.absolute @@ -72,18 +77,31 @@ class IntegrationTestDependencies( private val hasTests: Boolean, private val hasBenches: Boolean, ) : LibRsCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig override fun section(section: LibRsSection) = when (section) { is LibRsSection.Body -> testDependenciesOnly { if (hasTests) { + val smithyAsync = CargoDependency.smithyAsync(codegenContext.runtimeConfig) + .copy(features = setOf("test-util"), scope = DependencyScope.Dev) val smithyClient = CargoDependency.smithyClient(codegenContext.runtimeConfig) + .copy(features = setOf("test-util", "wiremock"), scope = DependencyScope.Dev) + val smithyTypes = CargoDependency.smithyTypes(codegenContext.runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev) + addDependency(smithyAsync) addDependency(smithyClient) + addDependency(smithyTypes) addDependency(CargoDependency.smithyProtocolTestHelpers(codegenContext.runtimeConfig)) addDependency(SerdeJson) addDependency(Tokio) addDependency(FuturesUtil) addDependency(Tracing.toDevDependency()) addDependency(TracingSubscriber) + + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + addDependency(smithyRuntime(runtimeConfig).copy(features = setOf("test-util"), scope = DependencyScope.Dev)) + addDependency(smithyRuntimeApi(runtimeConfig).copy(features = setOf("test-util"), scope = DependencyScope.Dev)) + addDependency(awsRuntime(runtimeConfig).toDevDependency().withFeature("test-util")) + } } if (hasBenches) { addDependency(Criterion) @@ -99,6 +117,7 @@ class IntegrationTestDependencies( private fun serviceSpecificCustomizations(): List = when (moduleName) { "transcribestreaming" -> listOf(TranscribeTestDependencies()) "s3" -> listOf(S3TestDependencies(codegenContext)) + "dynamodb" -> listOf(DynamoDbTestDependencies()) else -> emptyList() } } @@ -112,6 +131,13 @@ class TranscribeTestDependencies : LibRsCustomization() { } } +class DynamoDbTestDependencies : LibRsCustomization() { + override fun section(section: LibRsSection): Writable = + writable { + addDependency(Approx) + } +} + class S3TestDependencies(private val codegenContext: ClientCodegenContext) : LibRsCustomization() { override fun section(section: LibRsSection): Writable = writable { @@ -119,16 +145,10 @@ class S3TestDependencies(private val codegenContext: ClientCodegenContext) : Lib addDependency(BytesUtils.toDevDependency()) addDependency(FastRand.toDevDependency()) addDependency(HdrHistogram) + addDependency(HttpBody.toDevDependency()) addDependency(Smol) addDependency(TempFile) addDependency(TracingAppender) addDependency(TracingTest) - - // TODO(enableNewSmithyRuntime): These additional dependencies may not be needed anymore when removing this flag - // depending on if the sra-test is kept around or not. - if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { - addDependency(CargoDependency.smithyRuntime(codegenContext.runtimeConfig).toDevDependency()) - addDependency(CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toDevDependency()) - } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt new file mode 100644 index 0000000000..1fdbfcb06f --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -0,0 +1,116 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.docs +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.util.letIf + +class InvocationIdDecorator : ClientCodegenDecorator { + override val name: String get() = "InvocationIdDecorator" + override val order: Byte get() = 0 + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(InvocationIdRuntimePluginCustomization(codegenContext)) + } + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(InvocationIdConfigCustomization(codegenContext)) + } +} + +private class InvocationIdRuntimePluginCustomization( + private val codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + private val codegenScope = arrayOf( + "InvocationIdInterceptor" to awsRuntime.resolve("invocation_id::InvocationIdInterceptor"), + ) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate("#{InvocationIdInterceptor}::new()", *codegenScope) + } + } + } +} + +const val GENERATOR_DOCS: String = + "The invocation ID generator generates ID values for the `amz-sdk-invocation-id` header. " + + "By default, this will be a random UUID. Overriding it may be useful in tests that " + + "examine the HTTP request and need to be deterministic." + +private class InvocationIdConfigCustomization( + codegenContext: ClientCodegenContext, +) : ConfigCustomization() { + private val awsRuntime = AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) + private val codegenScope = arrayOf( + *preludeScope, + "InvocationIdGenerator" to awsRuntime.resolve("invocation_id::InvocationIdGenerator"), + "SharedInvocationIdGenerator" to awsRuntime.resolve("invocation_id::SharedInvocationIdGenerator"), + ) + + override fun section(section: ServiceConfig): Writable = writable { + when (section) { + is ServiceConfig.BuilderImpl -> { + docs("Overrides the default invocation ID generator.\n\n$GENERATOR_DOCS") + rustTemplate( + """ + pub fn invocation_id_generator(mut self, gen: impl #{InvocationIdGenerator} + 'static) -> Self { + self.set_invocation_id_generator(#{Some}(#{SharedInvocationIdGenerator}::new(gen))); + self + } + """, + *codegenScope, + ) + + docs("Overrides the default invocation ID generator.\n\n$GENERATOR_DOCS") + rustTemplate( + """ + pub fn set_invocation_id_generator(&mut self, gen: #{Option}<#{SharedInvocationIdGenerator}>) -> &mut Self { + self.config.store_or_unset(gen); + self + } + """, + *codegenScope, + ) + } + + is ServiceConfig.ConfigImpl -> { + docs("Returns the invocation ID generator if one was given in config.\n\n$GENERATOR_DOCS") + rustTemplate( + """ + pub fn invocation_id_generator(&self) -> #{Option}<#{SharedInvocationIdGenerator}> { + self.config.load::<#{SharedInvocationIdGenerator}>().cloned() + } + """, + *codegenScope, + ) + } + + else -> {} + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt new file mode 100644 index 0000000000..56092dab81 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt @@ -0,0 +1,44 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.letIf + +class RecursionDetectionDecorator : ClientCodegenDecorator { + override val name: String get() = "RecursionDetectionDecorator" + override val order: Byte get() = 0 + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(RecursionDetectionRuntimePluginCustomization(codegenContext)) + } +} + +private class RecursionDetectionRuntimePluginCustomization( + private val codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rust( + "#T::new()", + AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) + .resolve("recursion_detection::RecursionDetectionInterceptor"), + ) + } + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index bf116c9269..5dacc952bd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -13,18 +13,18 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.extendIf @@ -116,7 +116,7 @@ class RegionDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { if (usesRegion(codegenContext)) { - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rust("pub use #T::Region;", region(codegenContext.runtimeConfig)) } } @@ -131,7 +131,14 @@ class RegionDecorator : ClientCodegenDecorator { override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? { return when (parameter.builtIn) { Builtins.REGION.builtIn -> writable { - rust("$configRef.region.as_ref().map(|r|r.as_ref().to_owned())") + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + "$configRef.load::<#{Region}>().map(|r|r.as_ref().to_owned())", + "Region" to region(codegenContext.runtimeConfig).resolve("Region"), + ) + } else { + rust("$configRef.region.as_ref().map(|r|r.as_ref().to_owned())") + } } else -> null } @@ -153,27 +160,52 @@ class RegionDecorator : ClientCodegenDecorator { } } -class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization() { +class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() { private val region = region(codegenContext.runtimeConfig) private val moduleUseName = codegenContext.moduleUseName() - private val codegenScope = arrayOf("Region" to region.resolve("Region")) + private val runtimeMode = codegenContext.smithyRuntimeMode + private val codegenScope = arrayOf( + *preludeScope, + "Region" to region.resolve("Region"), + ) override fun section(section: ServiceConfig) = writable { when (section) { - ServiceConfig.ConfigStruct -> rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) - ServiceConfig.ConfigImpl -> rustTemplate( - """ - /// Returns the AWS region, if it was provided. - pub fn region(&self) -> Option<&#{Region}> { - self.region.as_ref() + ServiceConfig.ConfigStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate("pub(crate) region: #{Option}<#{Region}>,", *codegenScope) } - """, - *codegenScope, - ) + } + ServiceConfig.ConfigImpl -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Returns the AWS region, if it was provided. + pub fn region(&self) -> #{Option}<&#{Region}> { + self.config.load::<#{Region}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns the AWS region, if it was provided. + pub fn region(&self) -> #{Option}<&#{Region}> { + self.region.as_ref() + } + """, + *codegenScope, + ) + } + } - ServiceConfig.BuilderStruct -> - rustTemplate("region: Option<#{Region}>,", *codegenScope) + ServiceConfig.BuilderStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate("pub(crate) region: #{Option}<#{Region}>,", *codegenScope) + } + } - ServiceConfig.BuilderImpl -> + ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Sets the AWS region to use when making requests. @@ -187,18 +219,44 @@ class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization /// .region(Region::new("us-east-1")) /// .build(); /// ``` - pub fn region(mut self, region: impl Into>) -> Self { - self.region = region.into(); + pub fn region(mut self, region: impl #{Into}<#{Option}<#{Region}>>) -> Self { + self.set_region(region.into()); self } """, *codegenScope, ) - ServiceConfig.BuilderBuild -> rustTemplate( - """region: self.region,""", - *codegenScope, - ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Sets the AWS region to use when making requests. + pub fn set_region(&mut self, region: #{Option}<#{Region}>) -> &mut Self { + self.config.store_or_unset(region); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the AWS region to use when making requests. + pub fn set_region(&mut self, region: #{Option}<#{Region}>) -> &mut Self { + self.region = region; + self + } + """, + *codegenScope, + ) + } + } + + ServiceConfig.BuilderBuild -> { + if (runtimeMode.generateMiddleware) { + rust("region: self.region,") + } + } else -> emptySection } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index 0d488fb21f..16f0644496 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -8,12 +8,14 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.util.letIf class RetryClassifierDecorator : ClientCodegenDecorator { override val name: String = "RetryPolicy" @@ -23,13 +25,19 @@ class RetryClassifierDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List { - return baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig) - } + ): List = + (baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig)).letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + OperationRetryClassifiersFeature( + codegenContext, + operation, + ) + } } class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { - override fun retryType(): RuntimeType = AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier") + override fun retryType(): RuntimeType = + AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier") + override fun section(section: OperationSection) = when (section) { is OperationSection.FinalizeOperation -> writable { rust( @@ -41,3 +49,44 @@ class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : Operati else -> emptySection } } + +class OperationRetryClassifiersFeature( + codegenContext: ClientCodegenContext, + operation: OperationShape, +) : OperationCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + private val smithyRuntime = RuntimeType.smithyRuntime(runtimeConfig) + private val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) + private val codegenScope = arrayOf( + // Classifiers + "SmithyErrorClassifier" to smithyRuntime.resolve("client::retries::classifier::SmithyErrorClassifier"), + "AmzRetryAfterHeaderClassifier" to awsRuntime.resolve("retries::classifier::AmzRetryAfterHeaderClassifier"), + "ModeledAsRetryableClassifier" to smithyRuntime.resolve("client::retries::classifier::ModeledAsRetryableClassifier"), + "AwsErrorCodeClassifier" to awsRuntime.resolve("retries::classifier::AwsErrorCodeClassifier"), + "HttpStatusCodeClassifier" to smithyRuntime.resolve("client::retries::classifier::HttpStatusCodeClassifier"), + // Other Types + "ClassifyRetry" to smithyRuntimeApi.resolve("client::retries::ClassifyRetry"), + "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), + "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operation), + "OrchestratorError" to smithyRuntimeApi.resolve("client::orchestrator::OrchestratorError"), + "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), + ) + + override fun section(section: OperationSection) = when (section) { + is OperationSection.RetryClassifier -> writable { + rustTemplate( + """ + .with_classifier(#{SmithyErrorClassifier}::<#{OperationError}>::new()) + .with_classifier(#{AmzRetryAfterHeaderClassifier}) + .with_classifier(#{ModeledAsRetryableClassifier}::<#{OperationError}>::new()) + .with_classifier(#{AwsErrorCodeClassifier}::<#{OperationError}>::new()) + .with_classifier(#{HttpStatusCodeClassifier}::default()) + """, + *codegenScope, + ) + } + + else -> emptySection + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt new file mode 100644 index 0000000000..e3d9fb2176 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt @@ -0,0 +1,51 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.letIf + +class RetryInformationHeaderDecorator : ClientCodegenDecorator { + override val name: String = "RetryInformationHeader" + override val order: Byte = 10 + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(AddRetryInformationHeaderInterceptors(codegenContext)) + } +} + +private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { + // Track the latency between client and server. + section.registerInterceptor(runtimeConfig, this) { + rust( + "#T::new()", + awsRuntime.resolve("service_clock_skew::ServiceClockSkewInterceptor"), + ) + } + + // Add request metadata to outgoing requests. Sets a header. + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("request_info::RequestInfoInterceptor")) + } + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt index 1d94cd6082..f3277d06d3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt @@ -77,7 +77,7 @@ class GenericSmithySdkConfigSettings : ClientCodegenDecorator { ${section.serviceConfigBuilder}.set_sleep_impl(${section.sdkConfig}.sleep_impl()); ${section.serviceConfigBuilder}.set_http_connector(${section.sdkConfig}.http_connector().cloned()); - + ${section.serviceConfigBuilder}.set_time_source(${section.sdkConfig}.time_source()); """, ) }, @@ -105,7 +105,7 @@ class SdkConfigDecorator : ClientCodegenDecorator { val codegenScope = arrayOf( "SdkConfig" to AwsRuntimeType.awsTypes(codegenContext.runtimeConfig).resolve("sdk_config::SdkConfig"), ) - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rustTemplate( """ impl From<&#{SdkConfig}> for Builder { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt new file mode 100644 index 0000000000..279626e508 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -0,0 +1,154 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.aws.traits.auth.SigV4Trait +import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait +import software.amazon.smithy.model.knowledge.ServiceIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.util.hasEventStreamOperations +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.isInputEventStream +import software.amazon.smithy.rust.codegen.core.util.letIf + +class SigV4AuthDecorator : ClientCodegenDecorator { + override val name: String get() = "SigV4AuthDecorator" + override val order: Byte = 0 + + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthSchemeOptions: List, + ): List = baseAuthSchemeOptions.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + AuthSchemeOption.StaticAuthSchemeOption(SigV4Trait.ID) { + rustTemplate( + "#{scheme_id},", + "scheme_id" to AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) + .resolve("auth::sigv4::SCHEME_ID"), + ) + } + } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(AuthServiceRuntimePluginCustomization(codegenContext)) + } + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(AuthOperationCustomization(codegenContext)) + } +} + +private class AuthServiceRuntimePluginCustomization(private val codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope by lazy { + val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + arrayOf( + "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), + "SigV4AuthScheme" to awsRuntime.resolve("auth::sigv4::SigV4AuthScheme"), + "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), + "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), + "SharedAuthScheme" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::auth::SharedAuthScheme"), + ) + } + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { + val serviceHasEventStream = codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model) + if (serviceHasEventStream) { + // enable the aws-runtime `sign-eventstream` feature + addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol()) + } + section.registerAuthScheme(this) { + rustTemplate("#{SharedAuthScheme}::new(#{SigV4AuthScheme}::new())", *codegenScope) + } + } + + else -> {} + } + } +} + +private class AuthOperationCustomization(private val codegenContext: ClientCodegenContext) : OperationCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope by lazy { + val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + arrayOf( + "HttpSignatureType" to awsRuntime.resolve("auth::sigv4::HttpSignatureType"), + "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), + "SigV4OperationSigningConfig" to awsRuntime.resolve("auth::sigv4::SigV4OperationSigningConfig"), + "SigningOptions" to awsRuntime.resolve("auth::sigv4::SigningOptions"), + "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), + "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), + "SignableBody" to AwsRuntimeType.awsSigv4(runtimeConfig).resolve("http_request::SignableBody"), + ) + } + private val serviceIndex = ServiceIndex.of(codegenContext.model) + + override fun section(section: OperationSection): Writable = writable { + when (section) { + is OperationSection.AdditionalRuntimePluginConfig -> { + val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, section.operationShape) + if (authSchemes.containsKey(SigV4Trait.ID)) { + val unsignedPayload = section.operationShape.hasTrait() + val doubleUriEncode = unsignedPayload || !disableDoubleEncode(codegenContext.serviceShape) + val contentSha256Header = needsAmzSha256(codegenContext.serviceShape) + val normalizeUrlPath = !disableUriPathNormalization(codegenContext.serviceShape) + rustTemplate( + """ + let mut signing_options = #{SigningOptions}::default(); + signing_options.double_uri_encode = $doubleUriEncode; + signing_options.content_sha256_header = $contentSha256Header; + signing_options.normalize_uri_path = $normalizeUrlPath; + signing_options.payload_override = #{payload_override}; + + ${section.newLayerName}.store_put(#{SigV4OperationSigningConfig} { + region: None, + service: None, + signing_options, + }); + """, + *codegenScope, + "payload_override" to writable { + if (unsignedPayload) { + rustTemplate("Some(#{SignableBody}::UnsignedPayload)", *codegenScope) + } else if (section.operationShape.isInputEventStream(codegenContext.model)) { + // TODO(EventStream): Is this actually correct for all Event Stream operations? + rustTemplate("Some(#{SignableBody}::Bytes(&[]))", *codegenScope) + } else { + rust("None") + } + }, + ) + } + } + + else -> {} + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index 0676db868a..c7ba6f9c60 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -14,17 +14,18 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.EventStreamSigningConfig +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.extendIf @@ -32,6 +33,7 @@ import software.amazon.smithy.rust.codegen.core.util.hasEventStreamOperations import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.isInputEventStream +// TODO(enableNewSmithyRuntimeCleanup): Remove this decorator (superseded by SigV4AuthDecorator) /** * The SigV4SigningDecorator: * - adds a `signing_service()` method to `config` to return the default signing service @@ -53,6 +55,7 @@ class SigV4SigningDecorator : ClientCodegenDecorator { return baseCustomizations.extendIf(applies(codegenContext)) { SigV4SigningConfig( codegenContext.runtimeConfig, + codegenContext.smithyRuntimeMode, codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model), codegenContext.serviceShape.expectTrait(), ) @@ -76,44 +79,49 @@ class SigV4SigningDecorator : ClientCodegenDecorator { } class SigV4SigningConfig( - runtimeConfig: RuntimeConfig, + private val runtimeConfig: RuntimeConfig, + private val runtimeMode: SmithyRuntimeMode, private val serviceHasEventStream: Boolean, private val sigV4Trait: SigV4Trait, -) : EventStreamSigningConfig(runtimeConfig) { +) : ConfigCustomization() { private val codegenScope = arrayOf( - "SigV4Signer" to AwsRuntimeType.awsSigAuthEventStream(runtimeConfig).resolve("event_stream::SigV4Signer"), + "Region" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::Region"), + "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), + "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), ) - override fun configImplSection(): Writable { - return writable { - rustTemplate( - """ - /// The signature version 4 service signing name to use in the credential scope when signing requests. - /// - /// The signing service may be overridden by the `Endpoint`, or by specifying a custom - /// [`SigningService`](aws_types::SigningService) during operation construction - pub fn signing_service(&self) -> &'static str { - ${sigV4Trait.name.dq()} + override fun section(section: ServiceConfig): Writable = writable { + when (section) { + ServiceConfig.ConfigImpl -> { + if (runtimeMode.generateMiddleware && serviceHasEventStream) { + // enable the aws-sig-auth `sign-eventstream` feature + addDependency(AwsRuntimeType.awsSigAuthEventStream(runtimeConfig).toSymbol()) } - """, - *codegenScope, - ) - if (serviceHasEventStream) { - rustTemplate( - "#{signerFn:W}", - "signerFn" to - renderEventStreamSignerFn { propertiesName -> - writable { - rustTemplate( - """ - #{SigV4Signer}::new($propertiesName) - """, - *codegenScope, - ) - } - }, + rust( + """ + /// The signature version 4 service signing name to use in the credential scope when signing requests. + /// + /// The signing service may be overridden by the `Endpoint`, or by specifying a custom + /// [`SigningService`](aws_types::SigningService) during operation construction + pub fn signing_service(&self) -> &'static str { + ${sigV4Trait.name.dq()} + } + """, ) } + ServiceConfig.BuilderBuild -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + layer.store_put(#{SigningService}::from_static(${sigV4Trait.name.dq()})); + layer.load::<#{Region}>().cloned().map(|r| layer.store_put(#{SigningRegion}::from(r))); + """, + *codegenScope, + ) + } + } + + else -> emptySection } } } @@ -208,5 +216,3 @@ class SigV4SigningFeature( } } } - -fun RuntimeConfig.sigAuth() = awsRuntimeCrate("aws-sig-auth") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 38ff9f1324..bd38c4f7ed 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -10,21 +10,24 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.letIf /** * Inserts a UserAgent configuration into the operation @@ -37,7 +40,7 @@ class UserAgentDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { - return baseCustomizations + AppNameCustomization(codegenContext.runtimeConfig) + return baseCustomizations + AppNameCustomization(codegenContext) } override fun operationCustomizations( @@ -45,9 +48,17 @@ class UserAgentDecorator : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List { - return baseCustomizations + UserAgentFeature(codegenContext) + return baseCustomizations + UserAgentMutateOpRequest(codegenContext) } + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(AddApiMetadataIntoConfigBag(codegenContext)) + } + override fun extraSections(codegenContext: ClientCodegenContext): List { return listOf( adhocCustomization { section -> @@ -77,7 +88,7 @@ class UserAgentDecorator : ClientCodegenDecorator { ) } - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { // Re-export the app name so that it can be specified in config programmatically without an explicit dependency rustTemplate( "pub use #{AppName};", @@ -86,8 +97,26 @@ class UserAgentDecorator : ClientCodegenDecorator { } } - private class UserAgentFeature( - private val codegenContext: ClientCodegenContext, + private class AddApiMetadataIntoConfigBag(codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) + } + } + else -> emptySection + } + } + } + + // TODO(enableNewSmithyRuntimeCleanup): Remove this customization class + private class UserAgentMutateOpRequest( + codegenContext: ClientCodegenContext, ) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig @@ -114,15 +143,20 @@ class UserAgentDecorator : ClientCodegenDecorator { } } - private class AppNameCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { + private class AppNameCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val codegenScope = arrayOf( + *preludeScope, "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"), ) override fun section(section: ServiceConfig): Writable = when (section) { is ServiceConfig.BuilderStruct -> writable { - rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) + if (runtimeMode.generateMiddleware) { + rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope) + } } is ServiceConfig.BuilderImpl -> writable { @@ -136,41 +170,83 @@ class UserAgentDecorator : ClientCodegenDecorator { self.set_app_name(Some(app_name)); self } - - /// Sets the name of the app that is using the client. - /// - /// This _optional_ name is used to identify the application in the user agent that - /// gets sent along with requests. - pub fn set_app_name(&mut self, app_name: Option<#{AppName}>) -> &mut Self { - self.app_name = app_name; - self - } """, *codegenScope, ) + + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Sets the name of the app that is using the client. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn set_app_name(&mut self, app_name: #{Option}<#{AppName}>) -> &mut Self { + self.config.store_or_unset(app_name); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the name of the app that is using the client. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn set_app_name(&mut self, app_name: #{Option}<#{AppName}>) -> &mut Self { + self.app_name = app_name; + self + } + """, + *codegenScope, + ) + } } is ServiceConfig.BuilderBuild -> writable { - rust("app_name: self.app_name,") + if (runtimeMode.generateOrchestrator) { + rust("layer.store_put(#T.clone());", ClientRustModule.Meta.toType().resolve("API_METADATA")) + } else { + rust("app_name: self.app_name,") + } } is ServiceConfig.ConfigStruct -> writable { - rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) + if (runtimeMode.generateMiddleware) { + rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope) + } } is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Returns the name of the app that is using the client, if it was provided. - /// - /// This _optional_ name is used to identify the application in the user agent that - /// gets sent along with requests. - pub fn app_name(&self) -> Option<&#{AppName}> { - self.app_name.as_ref() - } - """, - *codegenScope, - ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Returns the name of the app that is using the client, if it was provided. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn app_name(&self) -> #{Option}<&#{AppName}> { + self.config.load::<#{AppName}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns the name of the app that is using the client, if it was provided. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn app_name(&self) -> #{Option}<&#{AppName}> { + self.app_name.as_ref() + } + """, + *codegenScope, + ) + } } else -> emptySection diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt index dfbd2ca597..98a37d778c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt @@ -9,12 +9,14 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.AuthTrait +import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.shapeId // / STS (and possibly other services) need to have auth manually set to [] -class DisabledAuthDecorator : ClientCodegenDecorator { +class DisabledAuthDecorator() : ClientCodegenDecorator { override val name: String = "OptionalAuth" override val order: Byte = 0 @@ -30,14 +32,21 @@ class DisabledAuthDecorator : ClientCodegenDecorator { private fun applies(service: ServiceShape) = optionalAuth.containsKey(service.id) - override fun transformModel(service: ServiceShape, model: Model): Model { + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model { if (!applies(service)) { return model } val optionalOperations = optionalAuth[service.id]!! return ModelTransformer.create().mapShapes(model) { if (optionalOperations.contains(it.id) && it is OperationShape) { - it.toBuilder().addTrait(AuthTrait(setOf())).build() + if (settings.codegenConfig.enableNewSmithyRuntime.generateOrchestrator) { + it.toBuilder().addTrait(OptionalAuthTrait()).build() + } else { + // In middleware, having an empty @auth trait completely disabled + // auth for an operation since not having credentials isn't an option + // in that implementation. + it.toBuilder().addTrait(AuthTrait(setOf())).build() + } } else { it } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index 8e957b3f59..4850d299ef 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -11,13 +11,18 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations @@ -55,6 +60,14 @@ class ServiceSpecificDecorator( // This kind of decorator gets explicitly added to the root sdk-codegen decorator override fun classpathDiscoverable(): Boolean = false + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthSchemeOptions: List, + ): List = baseAuthSchemeOptions.maybeApply(codegenContext.serviceShape) { + delegateTo.authOptions(codegenContext, operationShape, baseAuthSchemeOptions) + } + override fun builderCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -126,8 +139,27 @@ class ServiceSpecificDecorator( delegateTo.structureCustomizations(codegenContext, baseCustomizations) } - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = model.maybeApply(service) { - delegateTo.transformModel(service, model) + delegateTo.transformModel(service, model, settings) + } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.serviceRuntimePluginCustomizations(codegenContext, baseCustomizations) + } + + override fun protocolTestGenerator( + codegenContext: ClientCodegenContext, + baseGenerator: ProtocolTestGenerator, + ): ProtocolTestGenerator = baseGenerator.maybeApply(codegenContext.serviceShape) { + delegateTo.protocolTestGenerator(codegenContext, baseGenerator) + } + + override fun extraSections(codegenContext: ClientCodegenContext): List = + listOf().maybeApply(codegenContext.serviceShape) { + delegateTo.extraSections(codegenContext) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt index 5959918ef7..05087534b7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt @@ -8,25 +8,44 @@ package software.amazon.smithy.rustsdk.customize.apigateway import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rustsdk.InlineAwsDependency class ApiGatewayDecorator : ClientCodegenDecorator { override val name: String = "ApiGateway" override val order: Byte = 0 + // TODO(enableNewSmithyRuntimeCleanup): Delete when cleaning up middleware override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations + ApiGatewayAddAcceptHeader() + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateMiddleware) { + it + ApiGatewayAddAcceptHeader() + } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + ApiGatewayAcceptHeaderInterceptorCustomization(codegenContext) + } } -class ApiGatewayAddAcceptHeader : OperationCustomization() { +// TODO(enableNewSmithyRuntimeCleanup): Delete when cleaning up middleware +private class ApiGatewayAddAcceptHeader : OperationCustomization() { override fun section(section: OperationSection): Writable = when (section) { is OperationSection.FinalizeOperation -> emptySection is OperationSection.OperationImplBlock -> emptySection @@ -39,6 +58,28 @@ class ApiGatewayAddAcceptHeader : OperationCustomization() { RuntimeType.Http, ) } + else -> emptySection } } + +private class ApiGatewayAcceptHeaderInterceptorCustomization(private val codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate( + "#{Interceptor}::default()", + "Interceptor" to RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "apigateway_interceptors", + additionalDependency = arrayOf( + CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig), + ), + ), + ).resolve("AcceptHeaderInterceptor"), + ) + } + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt index e788920e1d..693b2b572d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rustsdk.customize.ec2 import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator class Ec2Decorator : ClientCodegenDecorator { @@ -15,6 +16,6 @@ class Ec2Decorator : ClientCodegenDecorator { // EC2 incorrectly models primitive shapes as unboxed when they actually // need to be boxed for the API to work properly - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = EC2MakePrimitivesOptional.processModel(model) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt index 63ef01d1b1..6b18ca9116 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt @@ -7,13 +7,15 @@ package software.amazon.smithy.rustsdk.customize.glacier import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.inputShape +// TODO(enableNewSmithyRuntimeCleanup): Delete this file when cleaning up middleware. + class AccountIdAutofill : OperationCustomization() { override fun mutSelf(): Boolean = true override fun consumesSelf(): Boolean = false diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt index 8772de0ce9..093bd61061 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt @@ -5,14 +5,16 @@ package software.amazon.smithy.rustsdk.customize.glacier +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.dq +// TODO(enableNewSmithyRuntimeCleanup): Delete this file when cleaning up middleware. + class ApiVersionHeader( /** * ApiVersion diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index 5ba71e2f20..ee3f18c175 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -6,10 +6,41 @@ package software.amazon.smithy.rustsdk.customize.glacier import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rustsdk.AwsCargoDependency +import software.amazon.smithy.rustsdk.InlineAwsDependency +/** + * Glacier has three required customizations: + * + * 1. Add the `x-amz-glacier-version` header to all requests + * 2. For operations with an `account_id` field, autofill that field with `-` if it is not set by the customer. + * This is required because the account ID is sent across as part of the URL, and `-` has been pre-established + * "no account ID" in the Glacier service. + * 3. The `UploadArchive` and `UploadMultipartPart` operations require tree hash headers to be + * calculated and added to the request. + * + * This decorator wires up these three customizations. + */ class GlacierDecorator : ClientCodegenDecorator { override val name: String = "Glacier" override val order: Byte = 0 @@ -18,9 +49,124 @@ class GlacierDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations + listOfNotNull( - ApiVersionHeader(codegenContext.serviceShape.version), - TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), - AccountIdAutofill.forOperation(operation, codegenContext.model), - ) + ): List { + return if (codegenContext.smithyRuntimeMode.generateMiddleware) { + baseCustomizations + listOfNotNull( + ApiVersionHeader(codegenContext.serviceShape.version), + TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), + AccountIdAutofill.forOperation(operation, codegenContext.model), + ) + } else { + baseCustomizations + GlacierOperationInterceptorsCustomization(codegenContext) + } + } + + override fun structureCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(GlacierAccountIdCustomization(codegenContext)) + } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(GlacierApiVersionCustomization(codegenContext)) + } +} + +/** Implements the `GlacierAccountId` trait for inputs that have an `account_id` field */ +private class GlacierAccountIdCustomization(private val codegenContext: ClientCodegenContext) : + StructureCustomization() { + override fun section(section: StructureSection): Writable = writable { + if (section is StructureSection.AdditionalTraitImpls && section.shape.inputWithAccountId()) { + val inlineModule = inlineModule(codegenContext.runtimeConfig) + rustTemplate( + """ + impl #{GlacierAccountId} for ${section.structName} { + fn account_id_mut(&mut self) -> &mut Option { + &mut self.account_id + } + } + """, + "GlacierAccountId" to inlineModule.resolve("GlacierAccountId"), + ) + } + } +} + +/** Adds the `x-amz-glacier-version` header to all requests */ +private class GlacierApiVersionCustomization(private val codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { + val apiVersion = codegenContext.serviceShape.version + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate( + "#{Interceptor}::new(${apiVersion.dq()})", + "Interceptor" to inlineModule(codegenContext.runtimeConfig).resolve("GlacierApiVersionInterceptor"), + ) + } + } + } +} + +/** + * Adds two interceptors: + * 1. `GlacierAccountIdAutofillInterceptor`: Uses the `GlacierAccountId` trait to correctly autofill the account ID field + * 2. `GlacierSetPrecomputedSignableBodyInterceptor`: Reuses the tree hash computation during signing rather than allowing + * the `aws-sigv4` module to recalculate the payload hash. + */ +private class GlacierOperationInterceptorsCustomization(private val codegenContext: ClientCodegenContext) : + OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + if (section is OperationSection.AdditionalInterceptors) { + val inputShape = codegenContext.model.expectShape(section.operationShape.inputShape) as StructureShape + val inlineModule = inlineModule(codegenContext.runtimeConfig) + if (inputShape.inputWithAccountId()) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate( + "#{Interceptor}::<#{Input}>::new()", + "Interceptor" to inlineModule.resolve("GlacierAccountIdAutofillInterceptor"), + "Input" to codegenContext.symbolProvider.toSymbol(inputShape), + ) + } + } + if (section.operationShape.requiresTreeHashHeader()) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate( + "#{Interceptor}::default()", + "Interceptor" to inlineModule.resolve("GlacierTreeHashHeaderInterceptor"), + ) + } + } + } + } } + +/** True when the operation requires tree hash headers */ +private fun OperationShape.requiresTreeHashHeader(): Boolean = + id == ShapeId.from("com.amazonaws.glacier#UploadArchive") || + id == ShapeId.from("com.amazonaws.glacier#UploadMultipartPart") + +private fun StructureShape.inputWithAccountId(): Boolean = + hasTrait() && members().any { it.memberName.lowercase() == "accountid" } + +private fun inlineModule(runtimeConfig: RuntimeConfig) = RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "glacier_interceptors", + additionalDependency = glacierInterceptorDependencies(runtimeConfig).toTypedArray(), + ), +) + +private fun glacierInterceptorDependencies(runtimeConfig: RuntimeConfig) = listOf( + AwsCargoDependency.awsRuntime(runtimeConfig), + AwsCargoDependency.awsSigv4(runtimeConfig), + CargoDependency.Bytes, + CargoDependency.Hex, + CargoDependency.Ring, + CargoDependency.smithyHttp(runtimeConfig), + CargoDependency.smithyRuntimeApi(runtimeConfig), +) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt index c97357a2fd..ff03c29dda 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt @@ -7,17 +7,19 @@ package software.amazon.smithy.rustsdk.customize.glacier import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rustsdk.InlineAwsDependency +// TODO(enableNewSmithyRuntimeCleanup): Delete this file when cleaning up middleware. + val TreeHashDependencies = listOf( CargoDependency.Ring, CargoDependency.TokioStream, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt index 007254c479..9e0888fa39 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt @@ -10,16 +10,18 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpLabelTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.letIf @@ -32,7 +34,7 @@ class Route53Decorator : ClientCodegenDecorator { private val logger: Logger = Logger.getLogger(javaClass.name) private val resourceShapes = setOf(ShapeId.from("com.amazonaws.route53#ResourceId"), ShapeId.from("com.amazonaws.route53#ChangeId")) - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isResourceId(shape)) { logger.info("Adding TrimResourceId trait to $shape") @@ -45,10 +47,14 @@ class Route53Decorator : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List { - val hostedZoneMember = - operation.inputShape(codegenContext.model).members().find { it.hasTrait() } + val inputShape = operation.inputShape(codegenContext.model) + val hostedZoneMember = inputShape.members().find { it.hasTrait() } return if (hostedZoneMember != null) { - baseCustomizations + TrimResourceIdCustomization(codegenContext.symbolProvider.toMemberName(hostedZoneMember)) + baseCustomizations + TrimResourceIdCustomization( + codegenContext, + inputShape, + codegenContext.symbolProvider.toMemberName(hostedZoneMember), + ) } else { baseCustomizations } @@ -59,25 +65,50 @@ class Route53Decorator : ClientCodegenDecorator { } } -class TrimResourceIdCustomization(private val fieldName: String) : OperationCustomization() { +class TrimResourceIdCustomization( + private val codegenContext: ClientCodegenContext, + private val inputShape: StructureShape, + private val fieldName: String, +) : + OperationCustomization() { override fun mutSelf(): Boolean = true override fun consumesSelf(): Boolean = true - private val trimResourceId = - RuntimeType.forInlineDependency( - InlineAwsDependency.forRustFile("route53_resource_id_preprocessor"), - ) - .resolve("trim_resource_id") - - override fun section(section: OperationSection): Writable { - return when (section) { - is OperationSection.MutateInput -> writable { + override fun section(section: OperationSection): Writable = writable { + when (section) { + // TODO(enableNewSmithyRuntimeCleanup): Delete this `MutateInput` section + is OperationSection.MutateInput -> { + val trimResourceId = + RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile("route53_resource_id_preprocessor_middleware"), + ) + .resolve("trim_resource_id") rustTemplate( "#{trim_resource_id}(&mut ${section.input}.$fieldName);", "trim_resource_id" to trimResourceId, ) } - else -> emptySection + + is OperationSection.AdditionalInterceptors -> { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) + val interceptor = + RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile("route53_resource_id_preprocessor"), + ).resolve("Route53ResourceIdInterceptor") + rustTemplate( + """ + #{Route53ResourceIdInterceptor}::new(|input: &mut #{Input}| { + &mut input.$fieldName + }) + """, + "Input" to codegenContext.symbolProvider.toSymbol(inputShape), + "Route53ResourceIdInterceptor" to interceptor, + "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), + ) + } + } + else -> {} } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 7e78f0d47a..72f128fb70 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -14,11 +14,15 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rulesengine.traits.EndpointTestCase +import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput +import software.amazon.smithy.rulesengine.traits.EndpointTestsTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientRestXmlFactory import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate @@ -31,7 +35,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml import software.amazon.smithy.rust.codegen.core.smithy.traits.AllowInvalidXmlRoot import software.amazon.smithy.rust.codegen.core.util.letIf -import software.amazon.smithy.rustsdk.endpoints.stripEndpointTrait import software.amazon.smithy.rustsdk.getBuiltIn import software.amazon.smithy.rustsdk.toWritable import java.util.logging.Logger @@ -50,36 +53,52 @@ class S3Decorator : ClientCodegenDecorator { override fun protocols( serviceId: ShapeId, - currentProtocols: ProtocolMap, - ): ProtocolMap = currentProtocols + mapOf( + currentProtocols: ProtocolMap, + ): ProtocolMap = currentProtocols + mapOf( RestXmlTrait.ID to ClientRestXmlFactory { protocolConfig -> S3ProtocolOverride(protocolConfig) }, ) - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isInInvalidXmlRootAllowList(shape)) { logger.info("Adding AllowInvalidXmlRoot trait to $it") (it as StructureShape).toBuilder().addTrait(AllowInvalidXmlRoot()).build() } - }.let(StripBucketFromHttpPath()::transform).let(stripEndpointTrait("RequestRoute")) + } + // the model has the bucket in the path + .let(StripBucketFromHttpPath()::transform) + // the tests in EP2 are incorrect and are missing request route + .let( + FilterEndpointTests( + operationInputFilter = { input -> + when (input.operationName) { + // it's impossible to express HostPrefix behavior in the current EP2 rules schema :-/ + // A handwritten test was written to cover this behavior + "WriteGetObjectResponse" -> null + else -> input + } + }, + )::transform, + ) override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { - return listOf(object : EndpointCustomization { - override fun setBuiltInOnServiceConfig(name: String, value: Node, configBuilderRef: String): Writable? { - if (!name.startsWith("AWS::S3")) { - return null - } - val builtIn = codegenContext.getBuiltIn(name) ?: return null - return writable { - rustTemplate( - "let $configBuilderRef = $configBuilderRef.${builtIn.name.rustName()}(#{value});", - "value" to value.toWritable(), - ) + return listOf( + object : EndpointCustomization { + override fun setBuiltInOnServiceConfig(name: String, value: Node, configBuilderRef: String): Writable? { + if (!name.startsWith("AWS::S3")) { + return null + } + val builtIn = codegenContext.getBuiltIn(name) ?: return null + return writable { + rustTemplate( + "let $configBuilderRef = $configBuilderRef.${builtIn.name.rustName()}(#{value});", + "value" to value.toWritable(), + ) + } } - } - }, + }, ) } @@ -88,9 +107,32 @@ class S3Decorator : ClientCodegenDecorator { } } +class FilterEndpointTests( + private val testFilter: (EndpointTestCase) -> EndpointTestCase? = { a -> a }, + private val operationInputFilter: (EndpointTestOperationInput) -> EndpointTestOperationInput? = { a -> a }, +) { + fun updateEndpointTests(endpointTests: List): List { + val filteredTests = endpointTests.mapNotNull { test -> testFilter(test) } + return filteredTests.map { test -> + val operationInputs = test.operationInputs + test.toBuilder().operationInputs(operationInputs.mapNotNull { operationInputFilter(it) }).build() + } + } + + fun transform(model: Model) = ModelTransformer.create().mapTraits(model) { _, trait -> + when (trait) { + is EndpointTestsTrait -> EndpointTestsTrait.builder().testCases(updateEndpointTests(trait.testCases)) + .version(trait.version).build() + + else -> trait + } + } +} + class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContext) { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( + *RuntimeType.preludeScope, "Bytes" to RuntimeType.Bytes, "ErrorMetadata" to RuntimeType.errorMetadata(runtimeConfig), "ErrorBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), @@ -103,7 +145,7 @@ class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContex override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType { return ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> rustBlockTemplate( - "pub fn $fnName(response_status: u16, _response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn $fnName(response_status: u16, _response_headers: &#{HeaderMap}, response_body: &[u8]) -> #{Result}<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rustTemplate( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt index 3534258b18..ce96a33b82 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName @@ -23,7 +24,7 @@ class S3ControlDecorator : ClientCodegenDecorator { override val name: String = "S3Control" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = stripEndpointTrait("AccountId")(model) override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt new file mode 100644 index 0000000000..8ead07d3bc --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk.customize.sso + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.SensitiveTrait +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.core.util.letIf +import java.util.logging.Logger + +class SSODecorator : ClientCodegenDecorator { + override val name: String = "SSO" + override val order: Byte = 0 + private val logger: Logger = Logger.getLogger(javaClass.name) + + private fun isAwsCredentials(shape: Shape): Boolean = shape.id == ShapeId.from("com.amazonaws.sso#RoleCredentials") + + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(isAwsCredentials(shape)) { + (shape as StructureShape).toBuilder().addTrait(SensitiveTrait()).build() + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt index a332dd3035..ed23c99d8f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt @@ -7,10 +7,13 @@ package software.amazon.smithy.rustsdk.customize.sts import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RetryableTrait +import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf @@ -25,7 +28,9 @@ class STSDecorator : ClientCodegenDecorator { shape is StructureShape && shape.hasTrait() && shape.id.namespace == "com.amazonaws.sts" && shape.id.name == "IDPCommunicationErrorException" - override fun transformModel(service: ServiceShape, model: Model): Model = + private fun isAwsCredentials(shape: Shape): Boolean = shape.id == ShapeId.from("com.amazonaws.sts#Credentials") + + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isIdpCommunicationError(shape)) { logger.info("Adding @retryable trait to $shape and setting its error type to 'server'") @@ -33,6 +38,8 @@ class STSDecorator : ClientCodegenDecorator { .removeTrait(ErrorTrait.ID) .addTrait(ErrorTrait("server")) .addTrait(RetryableTrait.builder().build()).build() + }.letIf(isAwsCredentials(shape)) { + (shape as StructureShape).toBuilder().addTrait(SensitiveTrait()).build() } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt new file mode 100644 index 0000000000..57c033421c --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -0,0 +1,126 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk.customize.timestream + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.toType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization +import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rustsdk.AwsCargoDependency +import software.amazon.smithy.rustsdk.DocSection +import software.amazon.smithy.rustsdk.InlineAwsDependency + +/** + * This decorator does two things: + * 1. Adds the `endpoint_discovery` inlineable + * 2. Adds a `with_endpoint_discovery_enabled` method on client that returns a wrapped client with endpoint discovery enabled + */ +class TimestreamDecorator : ClientCodegenDecorator { + override val name: String = "Timestream" + override val order: Byte = -1 + + private fun applies(codegenContext: ClientCodegenContext): Boolean = + codegenContext.smithyRuntimeMode.generateOrchestrator + + override fun extraSections(codegenContext: ClientCodegenContext): List = + emptyList().letIf(applies(codegenContext)) { + listOf( + adhocCustomization { + addDependency(AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency()) + rustTemplate( + """ + let config = aws_config::load_from_env().await; + // You MUST call `with_endpoint_discovery_enabled` to produce a working client for this service. + let ${it.clientName} = ${it.crateName}::Client::new(&config).with_endpoint_discovery_enabled().await; + """.replaceIndent(it.indent), + ) + }, + ) + } + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + if (!applies(codegenContext)) { + return + } + + val endpointDiscovery = InlineAwsDependency.forRustFile( + "endpoint_discovery", + Visibility.PUBLIC, + CargoDependency.Tokio.copy(scope = DependencyScope.Compile, features = setOf("sync")), + CargoDependency.smithyAsync(codegenContext.runtimeConfig).toDevDependency().withFeature("test-util"), + ) + rustCrate.withModule(ClientRustModule.client) { + // helper function to resolve an endpoint given a base client + rustTemplate( + """ + async fn resolve_endpoint(client: &crate::Client) -> Result<(#{Endpoint}, #{SystemTime}), #{ResolveEndpointError}> { + let describe_endpoints = + client.describe_endpoints().send().await.map_err(|e| { + #{ResolveEndpointError}::from_source("failed to call describe_endpoints", e) + })?; + let endpoint = describe_endpoints.endpoints().unwrap().get(0).unwrap(); + let expiry = client.conf().time_source().expect("checked when ep discovery was enabled").now() + + #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60); + Ok(( + #{Endpoint}::builder() + .url(format!("https://{}", endpoint.address().unwrap())) + .build(), + expiry, + )) + } + + impl Client { + /// Enable endpoint discovery for this client + /// + /// This method MUST be called to construct a working client. + pub async fn with_endpoint_discovery_enabled(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> { + let handle = self.handle.clone(); + + // The original client without endpoint discover gets moved into the endpoint discovery + // resolver since calls to DescribeEndpoint without discovery need to be made. + let client_without_discovery = self; + let (resolver, reloader) = #{endpoint_discovery}::create_cache( + move || { + let client = client_without_discovery.clone(); + async move { resolve_endpoint(&client).await } + }, + handle.conf.sleep_impl() + .expect("endpoint discovery requires the client config to have a sleep impl"), + handle.conf.time_source() + .expect("endpoint discovery requires the client config to have a time source"), + ).await?; + + let client_with_discovery = crate::Client::from_conf( + handle.conf.to_builder() + .endpoint_resolver(#{SharedEndpointResolver}::new(resolver)) + .build() + ); + Ok((client_with_discovery, reloader)) + } + } + """, + *RuntimeType.preludeScope, + "Arc" to RuntimeType.Arc, + "Duration" to RuntimeType.std.resolve("time::Duration"), + "SharedEndpointResolver" to RuntimeType.smithyHttp(codegenContext.runtimeConfig) + .resolve("endpoint::SharedEndpointResolver"), + "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), + "endpoint_discovery" to endpointDiscovery.toType(), + *Types(codegenContext.runtimeConfig).toArray(), + ) + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt index 9d620e11cb..f1a957be61 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt @@ -14,7 +14,7 @@ import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.clientInstantiator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.AttributeKind import software.amazon.smithy.rust.codegen.core.rustlang.escape @@ -120,10 +120,7 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test: private val moduleName = ctx.moduleUseName() private val endpointCustomizations = ctx.rootDecorator.endpointCustomizations(ctx) private val model = ctx.model - private val instantiator = clientInstantiator(ctx) - - private fun EndpointTestOperationInput.operationId() = - ShapeId.fromOptionalNamespace(ctx.serviceShape.id.namespace, operationName) + private val instantiator = ClientInstantiator(ctx) /** the Rust SDK doesn't support SigV4a — search endpoint.properties.authSchemes[].name */ private fun EndpointTestCase.isSigV4a() = @@ -183,7 +180,7 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test: private fun operationInvocation(testOperationInput: EndpointTestOperationInput) = writable { rust("client.${testOperationInput.operationName.toSnakeCase()}()") val operationInput = - model.expectShape(testOperationInput.operationId(), OperationShape::class.java).inputShape(model) + model.expectShape(ctx.operationId(testOperationInput), OperationShape::class.java).inputShape(model) testOperationInput.operationParams.members.forEach { (key, value) -> val member = operationInput.expectMember(key.value) rustTemplate( @@ -217,3 +214,6 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test: } } } + +fun ClientCodegenContext.operationId(testOperationInput: EndpointTestOperationInput): ShapeId = + this.serviceShape.allOperations.first { it.name == testOperationInput.operationName } diff --git a/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt b/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt new file mode 100644 index 0000000000..02ae9071d3 --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rustsdk.awsSdkIntegrationTest + +class SdkCodegenIntegrationTest { + companion object { + val model = """ + namespace test + + use aws.api#service + use aws.auth#sigv4 + use aws.protocols#restJson1 + use smithy.rules#endpointRuleSet + + @service(sdkId: "dontcare") + @restJson1 + @sigv4(name: "dontcare") + @auth([sigv4]) + @endpointRuleSet({ + "version": "1.0", + "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }], + "parameters": { + "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" }, + } + }) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + @optionalAuth + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + } + + @Test + fun smokeTestSdkCodegen() { + awsSdkIntegrationTest( + model, + generateOrchestrator = true, + ) { _, _ -> /* it should compile */ } + } + + @Test + fun smokeTestSdkCodegenMiddleware() { + awsSdkIntegrationTest( + model, + generateOrchestrator = false, + ) { _, _ -> /* it should compile */ } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt index e79617a716..0435e74259 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.HttpTrait +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.orNull @@ -27,9 +28,10 @@ class AwsPresigningDecoratorTest { } private fun testTransform(namespace: String, name: String, presignable: Boolean) { + val settings = testClientRustSettings() val decorator = AwsPresigningDecorator() val model = testOperation(namespace, name) - val transformed = decorator.transformModel(serviceShape(model), model) + val transformed = decorator.transformModel(serviceShape(model), model, settings) hasPresignableTrait(transformed, namespace, name) shouldBe presignable } @@ -65,6 +67,7 @@ class AwsPresigningDecoratorTest { class OverrideHttpMethodTransformTest { @Test fun `it should override the HTTP method for the listed operations`() { + val settings = testClientRustSettings() val model = """ namespace test use aws.protocols#restJson1 @@ -105,7 +108,7 @@ class OverrideHttpMethodTransformTest { ShapeId.from("test#One") to presignableOp, ShapeId.from("test#Two") to presignableOp, ), - ).transformModel(serviceShape, model) + ).transformModel(serviceShape, model, settings) val synthNamespace = "test.synthetic.aws.presigned" transformed.expectShape(ShapeId.from("$synthNamespace#One")).expectTrait().method shouldBe "GET" @@ -115,8 +118,10 @@ class OverrideHttpMethodTransformTest { } class MoveDocumentMembersToQueryParamsTransformTest { + @Test fun `it should move document members to query parameters for the listed operations`() { + val settings = testClientRustSettings() val model = """ namespace test use aws.protocols#restJson1 @@ -164,7 +169,7 @@ class MoveDocumentMembersToQueryParamsTransformTest { ) val transformed = AwsPresigningDecorator( mapOf(ShapeId.from("test#One") to presignableOp), - ).transformModel(serviceShape, model) + ).transformModel(serviceShape, model, settings) val index = HttpBindingIndex(transformed) index.getRequestBindings(ShapeId.from("test.synthetic.aws.presigned#One")).map { (key, value) -> diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt new file mode 100644 index 0000000000..960d3adbaf --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt @@ -0,0 +1,192 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import software.amazon.smithy.rust.codegen.core.testutil.tokioTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest + +internal class CredentialCacheConfigTest { + private val model = """ + namespace com.example + use aws.protocols#awsJson1_0 + use aws.api#service + use smithy.rules#endpointRuleSet + + @service(sdkId: "Some Value") + @awsJson1_0 + @endpointRuleSet({ + "version": "1.0", + "rules": [{ + "type": "endpoint", + "conditions": [{"fn": "isSet", "argv": [{"ref": "Region"}]}], + "endpoint": { "url": "https://example.com" } + }], + "parameters": { + "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" }, + } + }) + service HelloService { + operations: [SayHello], + version: "1" + } + + @optionalAuth + operation SayHello { input: TestInput } + structure TestInput { + foo: String, + } + """.asSmithyModel() + + @Test + fun `config override for credentials`() { + awsSdkIntegrationTest(model, generateOrchestrator = true) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *RuntimeType.preludeScope, + "Credentials" to AwsRuntimeType.awsCredentialTypesTestUtil(runtimeConfig) + .resolve("Credentials"), + "CredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig) + .resolve("cache::CredentialsCache"), + "ProvideCachedCredentials" to AwsRuntimeType.awsCredentialTypes(runtimeConfig) + .resolve("cache::ProvideCachedCredentials"), + "RuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugin"), + "SharedCredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig) + .resolve("cache::SharedCredentialsCache"), + "SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig) + .resolve("provider::SharedCredentialsProvider"), + ) + rustCrate.testModule { + unitTest( + "test_overriding_only_credentials_provider_should_panic", + additionalAttributes = listOf(Attribute.shouldPanic("also specify `.credentials_cache` when overriding credentials provider for the operation")), + ) { + rustTemplate( + """ + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder().build(); + let config_override = + crate::config::Config::builder().credentials_provider(#{Credentials}::for_tests()); + let sut = crate::config::ConfigOverrideRuntimePlugin::new( + config_override, + client_config.config, + &client_config.runtime_components, + ); + + // this should cause `panic!` + let _ = sut.config().unwrap(); + """, + *codegenScope, + ) + } + + unitTest( + "test_overriding_only_credentials_cache_should_panic", + additionalAttributes = listOf(Attribute.shouldPanic("also specify `.credentials_provider` when overriding credentials cache for the operation")), + ) { + rustTemplate( + """ + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder().build(); + let config_override = crate::config::Config::builder() + .credentials_cache(#{CredentialsCache}::no_caching()); + let sut = crate::config::ConfigOverrideRuntimePlugin::new( + config_override, + client_config.config, + &client_config.runtime_components, + ); + + // this should cause `panic!` + let _ = sut.config().unwrap(); + """, + *codegenScope, + ) + } + + tokioTest("test_overriding_cache_and_provider_leads_to_shared_credentials_cache_in_layer") { + rustTemplate( + """ + use #{ProvideCachedCredentials}; + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder() + .credentials_provider(#{Credentials}::for_tests()) + .build(); + let client_config_layer = client_config.config; + + // make sure test credentials are set in the client config level + assert_eq!(#{Credentials}::for_tests(), + client_config_layer + .load::<#{SharedCredentialsCache}>() + .unwrap() + .provide_cached_credentials() + .await + .unwrap() + ); + + let credentials = #{Credentials}::new( + "test", + "test", + #{None}, + #{None}, + "test", + ); + let config_override = crate::config::Config::builder() + .credentials_cache(#{CredentialsCache}::lazy()) + .credentials_provider(credentials.clone()); + let sut = crate::config::ConfigOverrideRuntimePlugin::new( + config_override, + client_config_layer, + &client_config.runtime_components, + ); + let sut_layer = sut.config().unwrap(); + + // make sure `.provide_cached_credentials` returns credentials set through `config_override` + assert_eq!(credentials, + sut_layer + .load::<#{SharedCredentialsCache}>() + .unwrap() + .provide_cached_credentials() + .await + .unwrap() + ); + """, + *codegenScope, + ) + } + + unitTest("test_not_overriding_cache_and_provider_leads_to_no_shared_credentials_cache_in_layer") { + rustTemplate( + """ + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder().build(); + let config_override = crate::config::Config::builder(); + let sut = crate::config::ConfigOverrideRuntimePlugin::new( + config_override, + client_config.config, + &client_config.runtime_components, + ); + let sut_layer = sut.config().unwrap(); + assert!(sut_layer + .load::<#{SharedCredentialsCache}>() + .is_none()); + """, + *codegenScope, + ) + } + } + } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt index df26efdfec..b405678853 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt @@ -5,12 +5,18 @@ package software.amazon.smithy.rustsdk -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode internal class CredentialProviderConfigTest { - @Test - fun `generates a valid config`() { - validateConfigCustomizations(CredentialProviderConfig(AwsTestRuntimeConfig)) + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val codegenContext = awsTestCodegenContext().withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations(codegenContext, CredentialProviderConfig(codegenContext)) } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointsCredentialsTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointsCredentialsTest.kt index 89e11618a6..0708035541 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointsCredentialsTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointsCredentialsTest.kt @@ -114,7 +114,7 @@ class EndpointsCredentialsTest { .credentials_provider(#{Credentials}::for_tests()) .build(); let client = $moduleName::Client::from_conf(conf); - let _ = client.custom_auth().send().await; + let _ = dbg!(client.custom_auth().send().await); let req = rcvr.expect_request(); let auth_header = req.headers().get("AUTHORIZATION").unwrap().to_str().unwrap(); assert!(auth_header.contains("/region-custom-auth/name-custom-auth/aws4_request"), "{}", auth_header); diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomizationTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomizationTest.kt index b97952e002..ca5e087b96 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomizationTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomizationTest.kt @@ -5,15 +5,20 @@ package software.amazon.smithy.rustsdk -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace class HttpConnectorConfigCustomizationTest { - @Test - fun `generates a valid config`() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { val project = TestWorkspace.testProject() - val codegenContext = awsTestCodegenContext() - validateConfigCustomizations(HttpConnectorConfigCustomization(codegenContext), project) + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val codegenContext = awsTestCodegenContext().withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations(codegenContext, HttpConnectorConfigCustomization(codegenContext), project) } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt new file mode 100644 index 0000000000..3a792e2a0a --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import SdkCodegenIntegrationTest +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.testutil.integrationTest + +class InvocationIdDecoratorTest { + @Test + fun customInvocationIdGenerator() { + awsSdkIntegrationTest(SdkCodegenIntegrationTest.model) { context, rustCrate -> + val rc = context.runtimeConfig + val moduleName = context.moduleUseName() + rustCrate.integrationTest("custom_invocation_id") { + rustTemplate( + """ + ##[#{tokio}::test] + async fn custom_invocation_id() { + ##[derive(::std::fmt::Debug)] + struct TestIdGen; + impl #{InvocationIdGenerator} for TestIdGen { + fn generate(&self) -> #{Result}<#{Option}<#{InvocationId}>, #{BoxError}> { + #{Ok}(#{Some}(#{InvocationId}::new("custom".into()))) + } + } + + let (conn, rx) = #{capture_request}(None); + let config = $moduleName::Config::builder() + .http_connector(conn) + .invocation_id_generator(TestIdGen) + .build(); + assert!(config.invocation_id_generator().is_some()); + + let client = $moduleName::Client::from_conf(config); + + let _ = dbg!(client.some_operation().send().await); + let request = rx.expect_request(); + assert_eq!("custom", request.headers().get("amz-sdk-invocation-id").unwrap()); + } + """, + *preludeScope, + "tokio" to CargoDependency.Tokio.toType(), + "InvocationIdGenerator" to AwsRuntimeType.awsRuntime(rc) + .resolve("invocation_id::InvocationIdGenerator"), + "InvocationId" to AwsRuntimeType.awsRuntime(rc) + .resolve("invocation_id::InvocationId"), + "BoxError" to RuntimeType.boxError(rc), + "capture_request" to RuntimeType.captureRequest(rc), + ) + } + } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/OperationInputTestGeneratorTests.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/OperationInputTestGeneratorTests.kt new file mode 100644 index 0000000000..2fde9a6866 --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/OperationInputTestGeneratorTests.kt @@ -0,0 +1,71 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import software.amazon.smithy.model.Model +import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rustsdk.endpoints.operationId + +class OperationInputTestGeneratorTests { + @Test + fun `finds operation shape by name`() { + val prefix = "\$version: \"2\"" + val operationModel = """ + $prefix + namespace operations + + operation Ping {} + """.trimIndent() + val serviceModel = """ + $prefix + namespace service + + use operations#Ping + + service MyService { + operations: [Ping] + } + """.trimIndent() + + val model = Model.assembler() + .discoverModels() + .addUnparsedModel("operation.smithy", operationModel) + .addUnparsedModel("main.smithy", serviceModel) + .assemble() + .unwrap() + + val context = testClientCodegenContext(model) + val testOperationInput = EndpointTestOperationInput.builder() + .operationName("Ping") + .build() + + val operationId = context.operationId(testOperationInput) + assertEquals("operations#Ping", operationId.toString()) + } + + @Test + fun `fails for operation name not found`() { + val model = """ + namespace test + operation Ping {} + service MyService { + operations: [Ping] + } + """.trimIndent().asSmithyModel() + + val context = testClientCodegenContext(model) + val testOperationInput = EndpointTestOperationInput.builder() + .operationName("Pong") + .build() + + assertThrows { context.operationId(testOperationInput) } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/RegionProviderConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/RegionProviderConfigTest.kt index 9d2e865a64..1160f322ef 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/RegionProviderConfigTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/RegionProviderConfigTest.kt @@ -5,22 +5,27 @@ package software.amazon.smithy.rustsdk -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.rustSettings internal class RegionProviderConfigTest { - @Test - fun `generates a valid config`() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { val project = TestWorkspace.testProject() + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) val codegenContext = awsTestCodegenContext( settings = testClientRustSettings( moduleName = project.rustSettings().moduleName, runtimeConfig = AwsTestRuntimeConfig, ), - ) - validateConfigCustomizations(RegionProviderConfig(codegenContext), project) + ).withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations(codegenContext, RegionProviderConfig(codegenContext), project) } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt similarity index 52% rename from aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt rename to aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt index 9de7c91f65..fae4da0386 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt @@ -5,19 +5,32 @@ package software.amazon.smithy.rustsdk -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource import software.amazon.smithy.aws.traits.auth.SigV4Trait +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.stubConfigProject +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest -internal class SigV4SigningCustomizationTest { - @Test - fun `generates a valid config`() { +internal class SigV4SigningDecoratorTest { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val codegenContext = awsTestCodegenContext( + settings = testClientRustSettings( + runtimeConfig = AwsTestRuntimeConfig, + ), + ).withSmithyRuntimeMode(smithyRuntimeMode) val project = stubConfigProject( + codegenContext, SigV4SigningConfig( - AwsTestRuntimeConfig, + codegenContext.runtimeConfig, + codegenContext.smithyRuntimeMode, true, SigV4Trait.builder().name("test-service").build(), ), diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt index fc40eec7cd..44ed5461a9 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest @@ -17,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.util.letIf import java.io.File // In aws-sdk-codegen, the working dir when gradle runs tests is actually `./aws`. So, to find the smithy runtime, we need @@ -35,13 +37,16 @@ fun awsTestCodegenContext(model: Model? = null, settings: ClientRustSettings? = settings = settings ?: testClientRustSettings(runtimeConfig = AwsTestRuntimeConfig), ) +// TODO(enableNewSmithyRuntimeCleanup): Remove generateOrchestrator once the runtime switches to the orchestrator fun awsSdkIntegrationTest( model: Model, + generateOrchestrator: Boolean = true, test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> }, ) = clientIntegrationTest( model, IntegrationTestParams( + cargoCommand = "cargo test --features test-util", runtimeConfig = AwsTestRuntimeConfig, additionalSettings = ObjectNode.builder().withMember( "customizationConfig", @@ -58,6 +63,9 @@ fun awsSdkIntegrationTest( "codegen", ObjectNode.builder() .withMember("includeFluentClient", false) + .letIf(generateOrchestrator) { + it.withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")) + } .build(), ).build(), ), diff --git a/aws/sdk/README.md b/aws/sdk/README.md index 160bea4da9..f7a48ebdaa 100644 --- a/aws/sdk/README.md +++ b/aws/sdk/README.md @@ -12,7 +12,7 @@ Generate an SDK: `./gradlew :aws:sdk:assemble` Generate, compile, and test an SDK: -`./gradlew :aws:sdk:build` +`./gradlew :aws:sdk:check` Run an SDK example: `./gradlew :aws:sdk:runExample --example dynamo-helloworld` diff --git a/aws/sdk/aws-models/config.json b/aws/sdk/aws-models/config.json index 7ca3f50448..f96f7b6f75 100644 --- a/aws/sdk/aws-models/config.json +++ b/aws/sdk/aws-models/config.json @@ -118,7 +118,7 @@ } }, "traits": { - "smithy.api#documentation": "

Indicates whether an Config rule is compliant based on\n\t\t\taccount ID, region, compliance, and rule name.

\n\t\t

A rule is compliant if all of the resources that the rule\n\t\t\tevaluated comply with it. It is noncompliant if any of these\n\t\t\tresources do not comply.

" + "smithy.api#documentation": "

Indicates whether an Config rule is compliant based on\n\t\t\taccount ID, region, compliance, and rule name.

\n

A rule is compliant if all of the resources that the rule\n\t\t\tevaluated comply with it. It is noncompliant if any of these\n\t\t\tresources do not comply.

" } }, "com.amazonaws.configservice#AggregateComplianceByConfigRuleList": { @@ -156,7 +156,7 @@ } }, "traits": { - "smithy.api#documentation": "

Provides aggregate compliance of the conformance pack. Indicates whether a conformance pack is compliant based on the name of the conformance pack, account ID, and region.

\n\t\t

A conformance pack is compliant if all of the rules in a conformance packs are compliant. It is noncompliant if any of the rules are not compliant.\n\t\t\tThe compliance status of a conformance pack is INSUFFICIENT_DATA only if all rules within a conformance pack cannot be evaluated due to insufficient data.\n\t\t\tIf some of the rules in a conformance pack are compliant but the compliance status of other rules in that same conformance pack is INSUFFICIENT_DATA, the conformance pack shows \n\t\t\tcompliant.

" + "smithy.api#documentation": "

Provides aggregate compliance of the conformance pack. Indicates whether a conformance pack is compliant based on the name of the conformance pack, account ID, and region.

\n

A conformance pack is compliant if all of the rules in a conformance packs are compliant. It is noncompliant if any of the rules are not compliant.\n\t\t\tThe compliance status of a conformance pack is INSUFFICIENT_DATA only if all rules within a conformance pack cannot be evaluated due to insufficient data.\n\t\t\tIf some of the rules in a conformance pack are compliant but the compliance status of other rules in that same conformance pack is INSUFFICIENT_DATA, the conformance pack shows \n\t\t\tcompliant.

" } }, "com.amazonaws.configservice#AggregateComplianceByConformancePackList": { @@ -223,7 +223,7 @@ } }, "traits": { - "smithy.api#documentation": "

Provides the number of compliant and noncompliant rules within a conformance pack.\n\t\t\tAlso provides the compliance status of the conformance pack and the total rule count which includes compliant rules, noncompliant rules, and rules that cannot be evaluated due to insufficient data.

\n\t\t\n\t\t

A conformance pack is compliant if all of the rules in a conformance packs are compliant. It is noncompliant if any of the rules are not compliant.\n\t\t\tThe compliance status of a conformance pack is INSUFFICIENT_DATA only if all rules within a conformance pack cannot be evaluated due to insufficient data.\n\t\t\tIf some of the rules in a conformance pack are compliant but the compliance status of other rules in that same conformance pack is INSUFFICIENT_DATA, the conformance pack shows compliant.

" + "smithy.api#documentation": "

Provides the number of compliant and noncompliant rules within a conformance pack.\n\t\t\tAlso provides the compliance status of the conformance pack and the total rule count which includes compliant rules, noncompliant rules, and rules that cannot be evaluated due to insufficient data.

\n

A conformance pack is compliant if all of the rules in a conformance packs are compliant. It is noncompliant if any of the rules are not compliant.\n\t\t\tThe compliance status of a conformance pack is INSUFFICIENT_DATA only if all rules within a conformance pack cannot be evaluated due to insufficient data.\n\t\t\tIf some of the rules in a conformance pack are compliant but the compliance status of other rules in that same conformance pack is INSUFFICIENT_DATA, the conformance pack shows compliant.

" } }, "com.amazonaws.configservice#AggregateConformancePackComplianceCount": { @@ -355,7 +355,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

The resource compliance status.

\n\t\t

For the AggregationEvaluationResult data type, Config supports only the COMPLIANT and\n\t\t\t\tNON_COMPLIANT. Config does not support the\n\t\t\t\tNOT_APPLICABLE and INSUFFICIENT_DATA\n\t\t\tvalue.

" + "smithy.api#documentation": "

The resource compliance status.

\n

For the AggregationEvaluationResult data type, Config supports only the COMPLIANT and\n\t\t\t\tNON_COMPLIANT. Config does not support the\n\t\t\t\tNOT_APPLICABLE and INSUFFICIENT_DATA\n\t\t\tvalue.

" } }, "ResultRecordedTime": { @@ -465,7 +465,7 @@ "LastUpdateStatus": { "target": "com.amazonaws.configservice#AggregatedSourceStatusType", "traits": { - "smithy.api#documentation": "

Filters the last updated status type.

\n\t\t
    \n
  • \n\t\t\t\t

    Valid value FAILED indicates errors while moving\n\t\t\t\t\tdata.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Valid value SUCCEEDED indicates the data was\n\t\t\t\t\tsuccessfully moved.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Valid value OUTDATED indicates the data is not the most\n\t\t\t\t\trecent.

    \n\t\t\t
  • \n
" + "smithy.api#documentation": "

Filters the last updated status type.

\n
    \n
  • \n

    Valid value FAILED indicates errors while moving\n\t\t\t\t\tdata.

    \n
  • \n
  • \n

    Valid value SUCCEEDED indicates the data was\n\t\t\t\t\tsuccessfully moved.

    \n
  • \n
  • \n

    Valid value OUTDATED indicates the data is not the most\n\t\t\t\t\trecent.

    \n
  • \n
" } }, "LastUpdateTime": { @@ -675,7 +675,7 @@ "configurationItemStatus": { "target": "com.amazonaws.configservice#ConfigurationItemStatus", "traits": { - "smithy.api#documentation": "

The configuration item status. The valid values are:

\n\t\t\n\t\t
    \n
  • \n

    OK – The resource configuration has been updated

    \n
  • \n
  • \n

    ResourceDiscovered – The resource was newly discovered

    \n
  • \n
  • \n

    ResourceNotRecorded – The resource was discovered but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
  • \n

    ResourceDeleted – The resource was deleted

    \n
  • \n
  • \n

    ResourceDeletedNotRecorded – The resource was deleted but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
\n\t\t \n

The CIs do not incur any cost.

\n
" + "smithy.api#documentation": "

The configuration item status. The valid values are:

\n
    \n
  • \n

    OK – The resource configuration has been updated

    \n
  • \n
  • \n

    ResourceDiscovered – The resource was newly discovered

    \n
  • \n
  • \n

    ResourceNotRecorded – The resource was discovered but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
  • \n

    ResourceDeleted – The resource was deleted

    \n
  • \n
  • \n

    ResourceDeletedNotRecorded – The resource was deleted but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
\n \n

The CIs do not incur any cost.

\n
" } }, "configurationStateId": { @@ -775,7 +775,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the current configuration items for resources that are present in your Config aggregator. The operation also returns a list of resources that are not processed in the current request. \n\t\t\tIf there are no unprocessed resources, the operation returns an empty unprocessedResourceIdentifiers list.

\n\t\t\n\t\t \n
    \n
  • \n

    The API does not return results for deleted resources.

    \n
  • \n
  • \n

    The API does not return tags and relationships.

    \n
  • \n
\n
" + "smithy.api#documentation": "

Returns the current configuration items for resources that are present in your Config aggregator. The operation also returns a list of resources that are not processed in the current request. \n\t\t\tIf there are no unprocessed resources, the operation returns an empty unprocessedResourceIdentifiers list.

\n \n
    \n
  • \n

    The API does not return results for deleted resources.

    \n
  • \n
  • \n

    The API does not return tags and relationships.

    \n
  • \n
\n
" } }, "com.amazonaws.configservice#BatchGetAggregateResourceConfigRequest": { @@ -795,6 +795,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#BatchGetAggregateResourceConfigResponse": { @@ -812,6 +815,9 @@ "smithy.api#documentation": "

A list of resource identifiers that were not processed with current scope. The list is empty if all the resources are processed.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#BatchGetResourceConfig": { @@ -831,7 +837,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the BaseConfigurationItem for one or more requested resources.\n\t\t\tThe operation also returns a list of resources that are\n\t\t\tnot processed in the current request. If there are no unprocessed\n\t\t\tresources, the operation returns an empty unprocessedResourceKeys\n\t\t\tlist.

\n\t\t \n\t\t\t
    \n
  • \n\t\t\t\t\t

    The API does not return results for deleted\n\t\t\t\t\t\tresources.

    \n\t\t\t\t
  • \n
  • \n\t\t\t\t\t

    The API does not return any tags for the requested\n\t\t\t\t\t\tresources. This information is filtered out of the\n\t\t\t\t\t\tsupplementaryConfiguration section of the API\n\t\t\t\t\t\tresponse.

    \n\t\t\t\t
  • \n
\n\t\t
" + "smithy.api#documentation": "

Returns the BaseConfigurationItem for one or more requested resources.\n\t\t\tThe operation also returns a list of resources that are\n\t\t\tnot processed in the current request. If there are no unprocessed\n\t\t\tresources, the operation returns an empty unprocessedResourceKeys\n\t\t\tlist.

\n \n
    \n
  • \n

    The API does not return results for deleted\n\t\t\t\t\t\tresources.

    \n
  • \n
  • \n

    The API does not return any tags for the requested\n\t\t\t\t\t\tresources. This information is filtered out of the\n\t\t\t\t\t\tsupplementaryConfiguration section of the API\n\t\t\t\t\t\tresponse.

    \n
  • \n
\n
" } }, "com.amazonaws.configservice#BatchGetResourceConfigRequest": { @@ -844,6 +850,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#BatchGetResourceConfigResponse": { @@ -861,6 +870,9 @@ "smithy.api#documentation": "

A list of resource keys that were not processed with the\n\t\t\tcurrent response. The unprocessesResourceKeys value is in the same\n\t\t\tform as ResourceKeys, so the value can be directly provided to a\n\t\t\tsubsequent BatchGetResourceConfig operation.\n\t\t\t\n\t\t\tIf there are no unprocessed resource keys, the response contains an\n\t\t\tempty unprocessedResourceKeys list.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#Boolean": { @@ -910,7 +922,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

Indicates whether an Amazon Web Services resource or Config rule is\n\t\t\tcompliant.

\n\t\t

A resource is compliant if it complies with all of the Config rules that evaluate it. A resource is noncompliant if it does\n\t\t\tnot comply with one or more of these rules.

\n\t\t

A rule is compliant if all of the resources that the rule\n\t\t\tevaluates comply with it. A rule is noncompliant if any of these\n\t\t\tresources do not comply.

\n\t\t

Config returns the INSUFFICIENT_DATA value\n\t\t\twhen no evaluation results are available for the Amazon Web Services resource or Config rule.

\n\t\t

For the Compliance data type, Config supports\n\t\t\tonly COMPLIANT, NON_COMPLIANT, and\n\t\t\t\tINSUFFICIENT_DATA values. Config does not\n\t\t\tsupport the NOT_APPLICABLE value for the\n\t\t\t\tCompliance data type.

" + "smithy.api#documentation": "

Indicates whether an Amazon Web Services resource or Config rule is\n\t\t\tcompliant.

\n

A resource is compliant if it complies with all of the Config rules that evaluate it. A resource is noncompliant if it does\n\t\t\tnot comply with one or more of these rules.

\n

A rule is compliant if all of the resources that the rule\n\t\t\tevaluates comply with it. A rule is noncompliant if any of these\n\t\t\tresources do not comply.

\n

Config returns the INSUFFICIENT_DATA value\n\t\t\twhen no evaluation results are available for the Amazon Web Services resource or Config rule.

\n

For the Compliance data type, Config supports\n\t\t\tonly COMPLIANT, NON_COMPLIANT, and\n\t\t\t\tINSUFFICIENT_DATA values. Config does not\n\t\t\tsupport the NOT_APPLICABLE value for the\n\t\t\t\tCompliance data type.

" } }, "ComplianceContributorCount": { @@ -1186,7 +1198,7 @@ "Scope": { "target": "com.amazonaws.configservice#Scope", "traits": { - "smithy.api#documentation": "

Defines which resources can trigger an evaluation for the rule.\n\t\t\tThe scope can include one or more resource types, a combination of\n\t\t\tone resource type and one resource ID, or a combination of a tag key\n\t\t\tand value. Specify a scope to constrain the resources that can\n\t\t\ttrigger an evaluation for the rule. If you do not specify a scope,\n\t\t\tevaluations are triggered when any resource in the recording group\n\t\t\tchanges.

\n\t\t \n

The scope can be empty.

\n
" + "smithy.api#documentation": "

Defines which resources can trigger an evaluation for the rule.\n\t\t\tThe scope can include one or more resource types, a combination of\n\t\t\tone resource type and one resource ID, or a combination of a tag key\n\t\t\tand value. Specify a scope to constrain the resources that can\n\t\t\ttrigger an evaluation for the rule. If you do not specify a scope,\n\t\t\tevaluations are triggered when any resource in the recording group\n\t\t\tchanges.

\n \n

The scope can be empty.

\n
" } }, "Source": { @@ -1205,19 +1217,19 @@ "MaximumExecutionFrequency": { "target": "com.amazonaws.configservice#MaximumExecutionFrequency", "traits": { - "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations\n\t\t\tfor a rule. You can specify a value for\n\t\t\t\tMaximumExecutionFrequency when:

\n\t\t
    \n
  • \n\t\t\t\t

    This is for an Config managed rule that is triggered at\n\t\t\t\t\ta periodic frequency.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Your custom rule is triggered when Config delivers\n\t\t\t\t\tthe configuration snapshot. For more information, see ConfigSnapshotDeliveryProperties.

    \n\t\t\t
  • \n
\n\n\n\n\t\t \n\t\t\t

By default, rules with a periodic trigger are evaluated\n\t\t\t\tevery 24 hours. To change the frequency, specify a valid value\n\t\t\t\tfor the MaximumExecutionFrequency\n\t\t\t\tparameter.

\n\t\t
" + "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations\n\t\t\tfor a rule. You can specify a value for\n\t\t\t\tMaximumExecutionFrequency when:

\n
    \n
  • \n

    This is for an Config managed rule that is triggered at\n\t\t\t\t\ta periodic frequency.

    \n
  • \n
  • \n

    Your custom rule is triggered when Config delivers\n\t\t\t\t\tthe configuration snapshot. For more information, see ConfigSnapshotDeliveryProperties.

    \n
  • \n
\n \n

By default, rules with a periodic trigger are evaluated\n\t\t\t\tevery 24 hours. To change the frequency, specify a valid value\n\t\t\t\tfor the MaximumExecutionFrequency\n\t\t\t\tparameter.

\n
" } }, "ConfigRuleState": { "target": "com.amazonaws.configservice#ConfigRuleState", "traits": { - "smithy.api#documentation": "

Indicates whether the Config rule is active or is currently\n\t\t\tbeing deleted by Config. It can also indicate the evaluation\n\t\t\tstatus for the Config rule.

\n\n\t\t

Config sets the state of the rule to\n\t\t\t\tEVALUATING temporarily after you use the\n\t\t\t\tStartConfigRulesEvaluation request to evaluate your\n\t\t\tresources against the Config rule.

\n\n\t\t

Config sets the state of the rule to\n\t\t\t\tDELETING_RESULTS temporarily after you use the\n\t\t\t\tDeleteEvaluationResults request to delete the\n\t\t\tcurrent evaluation results for the Config rule.

\n\n\t\t

Config temporarily sets the state of a rule to\n\t\t\t\tDELETING after you use the\n\t\t\t\tDeleteConfigRule request to delete the rule. After\n\t\t\tConfig deletes the rule, the rule and all of its evaluations are\n\t\t\terased and are no longer available.

" + "smithy.api#documentation": "

Indicates whether the Config rule is active or is currently\n\t\t\tbeing deleted by Config. It can also indicate the evaluation\n\t\t\tstatus for the Config rule.

\n

Config sets the state of the rule to\n\t\t\t\tEVALUATING temporarily after you use the\n\t\t\t\tStartConfigRulesEvaluation request to evaluate your\n\t\t\tresources against the Config rule.

\n

Config sets the state of the rule to\n\t\t\t\tDELETING_RESULTS temporarily after you use the\n\t\t\t\tDeleteEvaluationResults request to delete the\n\t\t\tcurrent evaluation results for the Config rule.

\n

Config temporarily sets the state of a rule to\n\t\t\t\tDELETING after you use the\n\t\t\t\tDeleteConfigRule request to delete the rule. After\n\t\t\tConfig deletes the rule, the rule and all of its evaluations are\n\t\t\terased and are no longer available.

" } }, "CreatedBy": { "target": "com.amazonaws.configservice#StringWithCharLimit256", "traits": { - "smithy.api#documentation": "

Service principal name of the service that created the\n\t\t\trule.

\n\t\t \n\t\t\t

The field is populated only if the service-linked rule is\n\t\t\t\tcreated by a service. The field is empty if you create your own\n\t\t\t\trule.

\n\t\t
" + "smithy.api#documentation": "

Service principal name of the service that created the\n\t\t\trule.

\n \n

The field is populated only if the service-linked rule is\n\t\t\t\tcreated by a service. The field is empty if you create your own\n\t\t\t\trule.

\n
" } }, "EvaluationModes": { @@ -1228,7 +1240,7 @@ } }, "traits": { - "smithy.api#documentation": "

Config rules evaluate the configuration settings of your Amazon Web Services resources. A rule can run when Config detects a configuration change to\n\t\t\tan Amazon Web Services resource or at a periodic frequency that you choose (for\n\t\t\texample, every 24 hours). There are two types of rules: Config Managed Rules and Config Custom Rules.\n\t\t\tManaged rules are predefined, customizable rules created by Config. For a list of managed rules, see\n\t\t\t\tList of Config\n\t\t\t\t\tManaged Rules.

\n\t\t\n\t\t

Custom rules are rules that you can create using either Guard or Lambda functions.\n\t\t\tGuard (Guard GitHub\n\t\t\t\tRepository) is a policy-as-code language that allows you to write policies that\n\t\t\tare enforced by Config Custom Policy rules. Lambda uses custom code that you upload to\n\t\t\tevaluate a custom rule. It is invoked by events that are published to it by an event source, which Config invokes when the custom rule is initiated.

\n\t\t\n\t\t

For more information about developing and using Config\n\t\t\trules, see Evaluating Amazon Web Services resource Configurations with Config\n\t\t\tin the Config Developer Guide.

\n\n\t\t \n\t\t\t

You can use the Amazon Web Services CLI and Amazon Web Services SDKs if you want to create\n\t\t\t\ta rule that triggers evaluations for your resources when Config delivers the configuration snapshot. For more\n\t\t\t\tinformation, see ConfigSnapshotDeliveryProperties.

\n\t\t
" + "smithy.api#documentation": "

Config rules evaluate the configuration settings of your Amazon Web Services resources. A rule can run when Config detects a configuration change to\n\t\t\tan Amazon Web Services resource or at a periodic frequency that you choose (for\n\t\t\texample, every 24 hours). There are two types of rules: Config Managed Rules and Config Custom Rules.

\n

Config Managed Rules are predefined,\n\t\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\t\tList of Config\n\t\t\t\t\tManaged Rules.

\n

Config Custom Rules are rules that you create from scratch. There are two ways to create Config custom rules: with Lambda functions\n\t\t\t\t( Lambda Developer Guide) and with Guard (Guard GitHub\n\t\t\t\t\t\tRepository), a policy-as-code language.\n\t\t\t\t\n\t\t\t\tConfig custom rules created with Lambda\n\t\t\t\tare called Config Custom Lambda Rules and Config custom rules created with\n\t\t\t\tGuard are called Config Custom Policy Rules.

\n

For more information about developing and using Config\n\t\t\trules, see Evaluating Resource with Config Rules\n\t\t\tin the Config Developer Guide.

\n \n

You can use the Amazon Web Services CLI and Amazon Web Services SDKs if you want to create\n\t\t\t\ta rule that triggers evaluations for your resources when Config delivers the configuration snapshot. For more\n\t\t\t\tinformation, see ConfigSnapshotDeliveryProperties.

\n
" } }, "com.amazonaws.configservice#ConfigRuleComplianceFilters": { @@ -1243,7 +1255,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

The rule compliance status.

\n\t\t

For the ConfigRuleComplianceFilters data type, Config supports only COMPLIANT and\n\t\t\t\tNON_COMPLIANT. Config does not support the\n\t\t\t\tNOT_APPLICABLE and the\n\t\t\t\tINSUFFICIENT_DATA values.

" + "smithy.api#documentation": "

The rule compliance status.

\n

For the ConfigRuleComplianceFilters data type, Config supports only COMPLIANT and\n\t\t\t\tNON_COMPLIANT. Config does not support the\n\t\t\t\tNOT_APPLICABLE and the\n\t\t\t\tINSUFFICIENT_DATA values.

" } }, "AccountId": { @@ -1373,7 +1385,7 @@ "target": "com.amazonaws.configservice#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether Config has evaluated your resources\n\t\t\tagainst the rule at least once.

\n\t\t
    \n
  • \n\t\t\t\t

    \n\t\t\t\t\t true - Config has evaluated your Amazon Web Services\n\t\t\t\t\tresources against the rule at least once.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    \n\t\t\t\t\t false - Config has not finished evaluating your Amazon Web Services resources against the\n\t\t\t\t\trule\n\t\t\t\t\tat least once.

    \n\t\t\t
  • \n
" + "smithy.api#documentation": "

Indicates whether Config has evaluated your resources\n\t\t\tagainst the rule at least once.

\n
    \n
  • \n

    \n true - Config has evaluated your Amazon Web Services\n\t\t\t\t\tresources against the rule at least once.

    \n
  • \n
  • \n

    \n false - Config has not finished evaluating your Amazon Web Services resources against the\n\t\t\t\t\trule\n\t\t\t\t\tat least once.

    \n
  • \n
" } }, "LastDebugLogDeliveryStatus": { @@ -1396,7 +1408,7 @@ } }, "traits": { - "smithy.api#documentation": "

Status information for your Config Managed rules and Config Custom Policy rules. The\n\t\t\tstatus includes information such as the last time the rule ran, the\n\t\t\tlast time it failed, and the related error for the last\n\t\t\tfailure.

\n\t\t

This action does not return status information about Config Custom Lambda rules.

" + "smithy.api#documentation": "

Status information for your Config Managed rules and Config Custom Policy rules. The\n\t\t\tstatus includes information such as the last time the rule ran, the\n\t\t\tlast time it failed, and the related error for the last\n\t\t\tfailure.

\n

This action does not return status information about Config Custom Lambda rules.

" } }, "com.amazonaws.configservice#ConfigRuleEvaluationStatusList": { @@ -1473,7 +1485,7 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for how often Config delivers\n\t\t\tconfiguration snapshots to the Amazon S3 bucket in your delivery\n\t\t\tchannel.

\n\n\t\t\n\t\t\n\n\t\t

The frequency for a rule that triggers evaluations for your\n\t\t\tresources when Config delivers the configuration snapshot is set\n\t\t\tby one of two values, depending on which is less frequent:

\n\n\t\t
    \n
  • \n\t\t\t\t

    The value for the deliveryFrequency\n\t\t\t\t\tparameter within the delivery channel configuration, which\n\t\t\t\t\tsets how often Config delivers configuration snapshots.\n\t\t\t\t\tThis value also sets how often Config invokes\n\t\t\t\t\tevaluations for Config rules.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The value for the\n\t\t\t\t\t\tMaximumExecutionFrequency parameter, which\n\t\t\t\t\tsets the maximum frequency with which Config invokes\n\t\t\t\t\tevaluations for the rule. For more information, see ConfigRule.

    \n\t\t\t
  • \n
\n\n\t\t

If the deliveryFrequency value is less frequent\n\t\t\tthan the MaximumExecutionFrequency value for a rule,\n\t\t\tConfig invokes the rule only as often as the\n\t\t\t\tdeliveryFrequency value.

\n\n\t\t
    \n
  1. \n\t\t\t\t

    For example, you want your rule to run evaluations when\n\t\t\t\t\tConfig delivers the configuration snapshot.

    \n\t\t\t
  2. \n
  3. \n\t\t\t\t

    You specify the MaximumExecutionFrequency\n\t\t\t\t\tvalue for Six_Hours.

    \n\t\t\t
  4. \n
  5. \n\t\t\t\t

    You then specify the delivery channel\n\t\t\t\t\t\tdeliveryFrequency value for\n\t\t\t\t\t\tTwentyFour_Hours.

    \n\t\t\t
  6. \n
  7. \n\t\t\t\t

    Because the value for deliveryFrequency is\n\t\t\t\t\tless frequent than MaximumExecutionFrequency,\n\t\t\t\t\tConfig invokes evaluations for the rule every 24 hours.\n\t\t\t\t

    \n\t\t\t
  8. \n
\n\n\n\t\t

You should set the MaximumExecutionFrequency value\n\t\t\tto be at least as frequent as the deliveryFrequency\n\t\t\tvalue. You can view the deliveryFrequency value by\n\t\t\tusing the DescribeDeliveryChannnels action.

\n\n\t\t

To update the deliveryFrequency with which Config delivers your configuration snapshots, use the\n\t\t\t\tPutDeliveryChannel action.

" + "smithy.api#documentation": "

Provides options for how often Config delivers\n\t\t\tconfiguration snapshots to the Amazon S3 bucket in your delivery\n\t\t\tchannel.

\n

The frequency for a rule that triggers evaluations for your\n\t\t\tresources when Config delivers the configuration snapshot is set\n\t\t\tby one of two values, depending on which is less frequent:

\n
    \n
  • \n

    The value for the deliveryFrequency\n\t\t\t\t\tparameter within the delivery channel configuration, which\n\t\t\t\t\tsets how often Config delivers configuration snapshots.\n\t\t\t\t\tThis value also sets how often Config invokes\n\t\t\t\t\tevaluations for Config rules.

    \n
  • \n
  • \n

    The value for the\n\t\t\t\t\t\tMaximumExecutionFrequency parameter, which\n\t\t\t\t\tsets the maximum frequency with which Config invokes\n\t\t\t\t\tevaluations for the rule. For more information, see ConfigRule.

    \n
  • \n
\n

If the deliveryFrequency value is less frequent\n\t\t\tthan the MaximumExecutionFrequency value for a rule,\n\t\t\tConfig invokes the rule only as often as the\n\t\t\t\tdeliveryFrequency value.

\n
    \n
  1. \n

    For example, you want your rule to run evaluations when\n\t\t\t\t\tConfig delivers the configuration snapshot.

    \n
  2. \n
  3. \n

    You specify the MaximumExecutionFrequency\n\t\t\t\t\tvalue for Six_Hours.

    \n
  4. \n
  5. \n

    You then specify the delivery channel\n\t\t\t\t\t\tdeliveryFrequency value for\n\t\t\t\t\t\tTwentyFour_Hours.

    \n
  6. \n
  7. \n

    Because the value for deliveryFrequency is\n\t\t\t\t\tless frequent than MaximumExecutionFrequency,\n\t\t\t\t\tConfig invokes evaluations for the rule every 24 hours.\n\t\t\t\t

    \n
  8. \n
\n

You should set the MaximumExecutionFrequency value\n\t\t\tto be at least as frequent as the deliveryFrequency\n\t\t\tvalue. You can view the deliveryFrequency value by\n\t\t\tusing the DescribeDeliveryChannnels action.

\n

To update the deliveryFrequency with which Config delivers your configuration snapshots, use the\n\t\t\t\tPutDeliveryChannel action.

" } }, "com.amazonaws.configservice#ConfigStreamDeliveryInfo": { @@ -1482,7 +1494,7 @@ "lastStatus": { "target": "com.amazonaws.configservice#DeliveryStatus", "traits": { - "smithy.api#documentation": "

Status of the last attempted delivery.

\n\t\t

\n\t\t\t Note Providing an SNS topic on a\n\t\t\t\tDeliveryChannel for Config is optional. If the SNS\n\t\t\tdelivery is turned off, the last status will be Not_Applicable.

" + "smithy.api#documentation": "

Status of the last attempted delivery.

\n

\n Note Providing an SNS topic on a\n\t\t\t\tDeliveryChannel for Config is optional. If the SNS\n\t\t\tdelivery is turned off, the last status will be Not_Applicable.

" } }, "lastErrorCode": { @@ -1619,7 +1631,7 @@ "configurationItemStatus": { "target": "com.amazonaws.configservice#ConfigurationItemStatus", "traits": { - "smithy.api#documentation": "

The configuration item status. The valid values are:

\n\t\t\n\t\t
    \n
  • \n

    OK – The resource configuration has been updated

    \n
  • \n
  • \n

    ResourceDiscovered – The resource was newly discovered

    \n
  • \n
  • \n

    ResourceNotRecorded – The resource was discovered but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
  • \n

    ResourceDeleted – The resource was deleted

    \n
  • \n
  • \n

    ResourceDeletedNotRecorded – The resource was deleted but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
\n\t\t \n

The CIs do not incur any cost.

\n
" + "smithy.api#documentation": "

The configuration item status. The valid values are:

\n
    \n
  • \n

    OK – The resource configuration has been updated

    \n
  • \n
  • \n

    ResourceDiscovered – The resource was newly discovered

    \n
  • \n
  • \n

    ResourceNotRecorded – The resource was discovered but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
  • \n

    ResourceDeleted – The resource was deleted

    \n
  • \n
  • \n

    ResourceDeletedNotRecorded – The resource was deleted but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
\n \n

The CIs do not incur any cost.

\n
" } }, "configurationStateId": { @@ -1631,7 +1643,7 @@ "configurationItemMD5Hash": { "target": "com.amazonaws.configservice#ConfigurationItemMD5Hash", "traits": { - "smithy.api#documentation": "

Unique MD5 hash that represents the configuration item's\n\t\t\tstate.

\n\t\t

You can use MD5 hash to compare the states of two or more\n\t\t\tconfiguration items that are associated with the same\n\t\t\tresource.

" + "smithy.api#documentation": "

Unique MD5 hash that represents the configuration item's\n\t\t\tstate.

\n

You can use MD5 hash to compare the states of two or more\n\t\t\tconfiguration items that are associated with the same\n\t\t\tresource.

" } }, "arn": { @@ -1685,7 +1697,7 @@ "relatedEvents": { "target": "com.amazonaws.configservice#RelatedEventList", "traits": { - "smithy.api#documentation": "

A list of CloudTrail event IDs.

\n\t\t

A populated field indicates that the current configuration was\n\t\t\tinitiated by the events recorded in the CloudTrail log. For more\n\t\t\tinformation about CloudTrail, see What Is CloudTrail.

\n\t\t

An empty field indicates that the current configuration was not\n\t\t\tinitiated by any event. As of Version 1.3, the relatedEvents field is empty. \n\t\t\tYou can access the LookupEvents API in the CloudTrail API Reference to retrieve the events for the resource.

" + "smithy.api#documentation": "

A list of CloudTrail event IDs.

\n

A populated field indicates that the current configuration was\n\t\t\tinitiated by the events recorded in the CloudTrail log. For more\n\t\t\tinformation about CloudTrail, see What Is CloudTrail.

\n

An empty field indicates that the current configuration was not\n\t\t\tinitiated by any event. As of Version 1.3, the relatedEvents field is empty. \n\t\t\tYou can access the LookupEvents API in the CloudTrail API Reference to retrieve the events for the resource.

" } }, "relationships": { @@ -1764,24 +1776,24 @@ "name": { "target": "com.amazonaws.configservice#RecorderName", "traits": { - "smithy.api#documentation": "

The name of the recorder. By default, Config automatically\n\t\t\tassigns the name \"default\" when creating the configuration recorder.\n\t\t\tYou cannot change the assigned name.

" + "smithy.api#documentation": "

The name of the configuration recorder. Config automatically assigns the name of \"default\" when creating the configuration recorder.

\n

You cannot change the name of the configuration recorder after it has been created. To change the configuration recorder name, you must delete it and create a new configuration recorder with a new name.

" } }, "roleARN": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

Amazon Resource Name (ARN) of the IAM role used to describe the\n\t\t\tAmazon Web Services resources associated with the account.

\n\t\t \n

While the API model does not require this field, the server will reject a request without a defined roleARN for the configuration recorder.

\n
" + "smithy.api#documentation": "

Amazon Resource Name (ARN) of the IAM role assumed by Config and used by the configuration recorder.

\n \n

While the API model does not require this field, the server will reject a request without a defined roleARN for the configuration recorder.

\n
\n \n

\n Pre-existing Config role\n

\n

If you have used an Amazon Web Services service that uses Config, such as Security Hub or\n\t\t\t\tControl Tower, and an Config role has already been created, make sure that the\n\t\t\t\tIAM role that you use when setting up Config keeps the same minimum\n\t\t\t\tpermissions as the already created Config role. You must do this so that the\n\t\t\t\tother Amazon Web Services service continues to run as expected.

\n

For example, if Control Tower has an IAM role that allows Config to read\n\t\t\t\tAmazon Simple Storage Service (Amazon S3) objects, make sure that the same permissions are granted\n\t\t\t\twithin the IAM role you use when setting up Config. Otherwise, it may\n\t\t\t\tinterfere with how Control Tower operates. For more information about IAM\n\t\t\t\troles for Config,\n\t\t\t\tsee \n Identity and Access Management for Config\n in the Config Developer Guide.\n\t\t\t

\n
" } }, "recordingGroup": { "target": "com.amazonaws.configservice#RecordingGroup", "traits": { - "smithy.api#documentation": "

Specifies the types of Amazon Web Services resources for which Config\n\t\t\trecords configuration changes.

" + "smithy.api#documentation": "

Specifies which resource types Config\n\t\t\trecords for configuration changes.

\n \n

\n High Number of Config Evaluations\n

\n

You may notice increased activity in your account during your initial month recording with Config when compared to subsequent months. During the\n\t\t\t\tinitial bootstrapping process, Config runs evaluations on all the resources in your account that you have selected\n\t\t\t\tfor Config to record.

\n

If you are running ephemeral workloads, you may see increased activity from Config as it records configuration changes associated with creating and deleting these\n\t\t\t\ttemporary resources. An ephemeral workload is a temporary use of computing resources that are loaded\n\t\t\t\tand run when needed. Examples include Amazon Elastic Compute Cloud (Amazon EC2)\n\t\t\t\tSpot Instances, Amazon EMR jobs, and Auto Scaling. If you want\n\t\t\t\tto avoid the increased activity from running ephemeral workloads, you can run these\n\t\t\t\ttypes of workloads in a separate account with Config turned off to avoid\n\t\t\t\tincreased configuration recording and rule evaluations.

\n
" } } }, "traits": { - "smithy.api#documentation": "

An object that represents the recording of configuration\n\t\t\tchanges of an Amazon Web Services resource.

" + "smithy.api#documentation": "

Records configuration changes to specified resource types.\n\t\t\tFor more information about the configuration recorder,\n\t\t\tsee \n Managing the Configuration Recorder\n in the Config Developer Guide.

" } }, "com.amazonaws.configservice#ConfigurationRecorderList": { @@ -1827,30 +1839,30 @@ "lastStatus": { "target": "com.amazonaws.configservice#RecorderStatus", "traits": { - "smithy.api#documentation": "

The last (previous) status of the recorder.

" + "smithy.api#documentation": "

The status of the latest recording event processed by the recorder.

" } }, "lastErrorCode": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

The error code indicating that the recording failed.

" + "smithy.api#documentation": "

The latest error code from when the recorder last failed.

" } }, "lastErrorMessage": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

The message indicating that the recording failed due to an\n\t\t\terror.

" + "smithy.api#documentation": "

The latest error message from when the recorder last failed.

" } }, "lastStatusChangeTime": { "target": "com.amazonaws.configservice#Date", "traits": { - "smithy.api#documentation": "

The time when the status was last changed.

" + "smithy.api#documentation": "

The time of the latest change in status of an recording event processed by the recorder.

" } } }, "traits": { - "smithy.api#documentation": "

The current status of the configuration recorder.

" + "smithy.api#documentation": "

The current status of the configuration recorder.

\n \n

For a detailed status of recording events over time, add your Config events to CloudWatch metrics and use CloudWatch metrics.

\n
" } }, "com.amazonaws.configservice#ConfigurationRecorderStatusList": { @@ -1883,7 +1895,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ConformancePackComplianceType", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT and NON_COMPLIANT. INSUFFICIENT_DATA is not supported.

" + "smithy.api#documentation": "

Filters the results by compliance.

\n

The allowed values are COMPLIANT and NON_COMPLIANT. INSUFFICIENT_DATA is not supported.

" } } }, @@ -1963,7 +1975,7 @@ "ConformancePackComplianceStatus": { "target": "com.amazonaws.configservice#ConformancePackComplianceType", "traits": { - "smithy.api#documentation": "

The status of the conformance pack. The allowed values are COMPLIANT, NON_COMPLIANT and INSUFFICIENT_DATA.

", + "smithy.api#documentation": "

The status of the conformance pack.

", "smithy.api#required": {} } } @@ -2046,13 +2058,13 @@ "DeliveryS3Bucket": { "target": "com.amazonaws.configservice#DeliveryS3Bucket", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n \n

This field is optional.

\n
" } }, "DeliveryS3KeyPrefix": { "target": "com.amazonaws.configservice#DeliveryS3KeyPrefix", "traits": { - "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n \n

This field is optional.

\n
" } }, "ConformancePackInputParameters": { @@ -2108,7 +2120,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ConformancePackComplianceType", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT and NON_COMPLIANT. INSUFFICIENT_DATA is not supported.

" + "smithy.api#documentation": "

Filters the results by compliance.

\n

The allowed values are COMPLIANT and NON_COMPLIANT. INSUFFICIENT_DATA is not supported.

" } }, "ResourceType": { @@ -2120,7 +2132,7 @@ "ResourceIds": { "target": "com.amazonaws.configservice#ConformancePackComplianceResourceIds", "traits": { - "smithy.api#documentation": "

Filters the results by resource IDs.

\n\t\t \n

This is valid only when you provide resource type. If there is no resource type, you will see an error.

\n
" + "smithy.api#documentation": "

Filters the results by resource IDs.

\n \n

This is valid only when you provide resource type. If there is no resource type, you will see an error.

\n
" } } }, @@ -2270,7 +2282,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ConformancePackComplianceType", "traits": { - "smithy.api#documentation": "

Compliance of the Config rule.

\n\t\t

The allowed values are COMPLIANT, NON_COMPLIANT, and INSUFFICIENT_DATA.

" + "smithy.api#documentation": "

Compliance of the Config rule.

" } }, "Controls": { @@ -2370,7 +2382,7 @@ "ConformancePackState": { "target": "com.amazonaws.configservice#ConformancePackState", "traits": { - "smithy.api#documentation": "

Indicates deployment status of conformance pack.

\n\t\t

Config sets the state of the conformance pack to:

\n\t\t
    \n
  • \n

    CREATE_IN_PROGRESS when a conformance pack creation is in progress for an account.

    \n
  • \n
  • \n

    CREATE_COMPLETE when a conformance pack has been successfully created in your account.

    \n
  • \n
  • \n

    CREATE_FAILED when a conformance pack creation failed in your account.

    \n
  • \n
  • \n

    DELETE_IN_PROGRESS when a conformance pack deletion is in progress.

    \n
  • \n
  • \n

    DELETE_FAILED when a conformance pack deletion failed in your account.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status of conformance pack.

\n

Config sets the state of the conformance pack to:

\n
    \n
  • \n

    CREATE_IN_PROGRESS when a conformance pack creation is in progress for an account.

    \n
  • \n
  • \n

    CREATE_COMPLETE when a conformance pack has been successfully created in your account.

    \n
  • \n
  • \n

    CREATE_FAILED when a conformance pack creation failed in your account.

    \n
  • \n
  • \n

    DELETE_IN_PROGRESS when a conformance pack deletion is in progress.

    \n
  • \n
  • \n

    DELETE_FAILED when a conformance pack deletion failed in your account.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -2437,7 +2449,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have specified a template that is invalid or supported.

", + "smithy.api#documentation": "

You have specified a template that is not valid or supported.

", "smithy.api#error": "client" } }, @@ -2541,6 +2553,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteConfigRule": { @@ -2560,7 +2575,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified Config rule and all of its evaluation\n\t\t\tresults.

\n\t\t

Config sets the state of a rule to DELETING\n\t\t\tuntil the deletion is complete. You cannot update a rule while it is\n\t\t\tin this state. If you make a PutConfigRule or\n\t\t\t\tDeleteConfigRule request for the rule, you will\n\t\t\treceive a ResourceInUseException.

\n\t\t

You can check the state of a rule by using the\n\t\t\t\tDescribeConfigRules request.

" + "smithy.api#documentation": "

Deletes the specified Config rule and all of its evaluation\n\t\t\tresults.

\n

Config sets the state of a rule to DELETING\n\t\t\tuntil the deletion is complete. You cannot update a rule while it is\n\t\t\tin this state. If you make a PutConfigRule or\n\t\t\t\tDeleteConfigRule request for the rule, you will\n\t\t\treceive a ResourceInUseException.

\n

You can check the state of a rule by using the\n\t\t\t\tDescribeConfigRules request.

" } }, "com.amazonaws.configservice#DeleteConfigRuleRequest": { @@ -2575,7 +2590,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteConfigurationAggregator": { @@ -2605,6 +2621,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteConfigurationRecorder": { @@ -2621,7 +2640,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the configuration recorder.

\n\t\t

After the configuration recorder is deleted, Config will\n\t\t\tnot record resource configuration changes until you create a new\n\t\t\tconfiguration recorder.

\n\t\t

This action does not delete the configuration information that\n\t\t\twas previously recorded. You will be able to access the previously\n\t\t\trecorded information by using the\n\t\t\t\tGetResourceConfigHistory action, but you will not\n\t\t\tbe able to access this information in the Config console until\n\t\t\tyou create a new configuration recorder.

" + "smithy.api#documentation": "

Deletes the configuration recorder.

\n

After the configuration recorder is deleted, Config will\n\t\t\tnot record resource configuration changes until you create a new\n\t\t\tconfiguration recorder.

\n

This action does not delete the configuration information that\n\t\t\twas previously recorded. You will be able to access the previously\n\t\t\trecorded information by using the\n\t\t\t\tGetResourceConfigHistory action, but you will not\n\t\t\tbe able to access this information in the Config console until\n\t\t\tyou create a new configuration recorder.

" } }, "com.amazonaws.configservice#DeleteConfigurationRecorderRequest": { @@ -2636,7 +2655,8 @@ } }, "traits": { - "smithy.api#documentation": "

The request object for the\n\t\t\t\tDeleteConfigurationRecorder action.

" + "smithy.api#documentation": "

The request object for the\n\t\t\t\tDeleteConfigurationRecorder action.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteConformancePack": { @@ -2656,7 +2676,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified conformance pack and all the Config rules, remediation actions, and all evaluation results within that \n\t\t\tconformance pack.

\n\t\t

Config sets the conformance pack to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\tYou cannot update a conformance pack while it is in this state.

" + "smithy.api#documentation": "

Deletes the specified conformance pack and all the Config rules, remediation actions, and all evaluation results within that \n\t\t\tconformance pack.

\n

Config sets the conformance pack to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\tYou cannot update a conformance pack while it is in this state.

" } }, "com.amazonaws.configservice#DeleteConformancePackRequest": { @@ -2669,6 +2689,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteDeliveryChannel": { @@ -2688,7 +2711,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the delivery channel.

\n\t\t

Before you can delete the delivery channel, you must stop the\n\t\t\tconfiguration recorder by using the StopConfigurationRecorder action.

" + "smithy.api#documentation": "

Deletes the delivery channel.

\n

Before you can delete the delivery channel, you must stop the\n\t\t\tconfiguration recorder by using the StopConfigurationRecorder action.

" } }, "com.amazonaws.configservice#DeleteDeliveryChannelRequest": { @@ -2703,7 +2726,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DeleteDeliveryChannel\n\t\t\taction. The action accepts the following data, in JSON format.\n\t\t

" + "smithy.api#documentation": "

The input for the DeleteDeliveryChannel\n\t\t\taction. The action accepts the following data, in JSON format.\n\t\t

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteEvaluationResults": { @@ -2738,14 +2762,16 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteEvaluationResultsResponse": { "type": "structure", "members": {}, "traits": { - "smithy.api#documentation": "

The output when you delete the evaluation results for the\n\t\t\tspecified Config rule.

" + "smithy.api#documentation": "

The output when you delete the evaluation results for the\n\t\t\tspecified Config rule.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DeleteOrganizationConfigRule": { @@ -2768,7 +2794,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified organization Config rule and all of its evaluation results from all member accounts in that organization.

\n\t

Only a management account and a delegated administrator account can delete an organization Config rule.\n\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added.

\n\t\t

Config sets the state of a rule to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\tYou cannot update a rule while it is in this state.

" + "smithy.api#documentation": "

Deletes the specified organization Config rule and all of its evaluation results from all member accounts in that organization.

\n

Only a management account and a delegated administrator account can delete an organization Config rule.\n\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added.

\n

Config sets the state of a rule to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\tYou cannot update a rule while it is in this state.

" } }, "com.amazonaws.configservice#DeleteOrganizationConfigRuleRequest": { @@ -2781,6 +2807,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteOrganizationConformancePack": { @@ -2803,7 +2832,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified organization conformance pack and all of the Config rules and remediation actions from\n\t\t\tall member accounts in that organization.

\n

Only a management account or a delegated administrator account can delete an organization conformance pack.\n\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\tListDelegatedAdministrator permissions are added.

\n\t\t\t

Config sets the state of a conformance pack to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

" + "smithy.api#documentation": "

Deletes the specified organization conformance pack and all of the Config rules and remediation actions from\n\t\t\tall member accounts in that organization.

\n

Only a management account or a delegated administrator account can delete an organization conformance pack.\n\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\tListDelegatedAdministrator permissions are added.

\n

Config sets the state of a conformance pack to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

" } }, "com.amazonaws.configservice#DeleteOrganizationConformancePackRequest": { @@ -2816,6 +2845,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeletePendingAggregationRequest": { @@ -2852,6 +2884,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteRemediationConfiguration": { @@ -2896,11 +2931,17 @@ "smithy.api#documentation": "

The type of a resource.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteRemediationConfigurationResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.configservice#DeleteRemediationExceptions": { "type": "operation", @@ -2916,7 +2957,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes one or more remediation exceptions mentioned in the resource keys.

\n\t\t \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
" + "smithy.api#documentation": "

Deletes one or more remediation exceptions mentioned in the resource keys.

\n \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
" } }, "com.amazonaws.configservice#DeleteRemediationExceptionsRequest": { @@ -2936,6 +2977,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteRemediationExceptionsResponse": { @@ -2947,6 +2991,9 @@ "smithy.api#documentation": "

Returns a list of failed delete remediation exceptions batch objects. Each object in the batch consists of a list of failed items and failure messages.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DeleteResourceConfig": { @@ -2986,6 +3033,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteRetentionConfiguration": { @@ -3018,6 +3068,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteStoredQuery": { @@ -3050,11 +3103,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteStoredQueryResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.configservice#DeliverConfigSnapshot": { "type": "operation", @@ -3076,7 +3135,7 @@ } ], "traits": { - "smithy.api#documentation": "

Schedules delivery of a configuration snapshot to the Amazon S3\n\t\t\tbucket in the specified delivery channel. After the delivery has\n\t\t\tstarted, Config sends the following notifications using an\n\t\t\tAmazon SNS topic that you have specified.

\n\t\t
    \n
  • \n\t\t\t\t

    Notification of the start of the delivery.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Notification of the completion of the delivery, if the\n\t\t\t\t\tdelivery was successfully completed.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Notification of delivery failure, if the delivery\n\t\t\t\t\tfailed.

    \n\t\t\t
  • \n
" + "smithy.api#documentation": "

Schedules delivery of a configuration snapshot to the Amazon S3\n\t\t\tbucket in the specified delivery channel. After the delivery has\n\t\t\tstarted, Config sends the following notifications using an\n\t\t\tAmazon SNS topic that you have specified.

\n
    \n
  • \n

    Notification of the start of the delivery.

    \n
  • \n
  • \n

    Notification of the completion of the delivery, if the\n\t\t\t\t\tdelivery was successfully completed.

    \n
  • \n
  • \n

    Notification of delivery failure, if the delivery\n\t\t\t\t\tfailed.

    \n
  • \n
" } }, "com.amazonaws.configservice#DeliverConfigSnapshotRequest": { @@ -3091,7 +3150,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DeliverConfigSnapshot\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the DeliverConfigSnapshot\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeliverConfigSnapshotResponse": { @@ -3105,7 +3165,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DeliverConfigSnapshot\n\t\t\taction, in JSON format.

" + "smithy.api#documentation": "

The output for the DeliverConfigSnapshot\n\t\t\taction, in JSON format.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DeliveryChannel": { @@ -3120,7 +3181,7 @@ "s3BucketName": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket to which Config delivers\n\t\t\tconfiguration snapshots and configuration history files.

\n\t\t

If you specify a bucket that belongs to another Amazon Web Services account,\n\t\t\tthat bucket must have policies that grant access permissions to Config. For more information, see Permissions for the Amazon S3 Bucket in the Config\n\t\t\tDeveloper Guide.

" + "smithy.api#documentation": "

The name of the Amazon S3 bucket to which Config delivers\n\t\t\tconfiguration snapshots and configuration history files.

\n

If you specify a bucket that belongs to another Amazon Web Services account,\n\t\t\tthat bucket must have policies that grant access permissions to Config. For more information, see Permissions for the Amazon S3 Bucket in the Config\n\t\t\tDeveloper Guide.

" } }, "s3KeyPrefix": { @@ -3138,7 +3199,7 @@ "snsTopicARN": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Amazon SNS topic to which\n\t\t\tConfig sends notifications about configuration\n\t\t\tchanges.

\n\t\t

If you choose a topic from another account, the topic must have\n\t\t\tpolicies that grant access permissions to Config. For more\n\t\t\tinformation, see Permissions for the Amazon SNS Topic in the Config\n\t\t\tDeveloper Guide.

" + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Amazon SNS topic to which\n\t\t\tConfig sends notifications about configuration\n\t\t\tchanges.

\n

If you choose a topic from another account, the topic must have\n\t\t\tpolicies that grant access permissions to Config. For more\n\t\t\tinformation, see Permissions for the Amazon SNS Topic in the Config\n\t\t\tDeveloper Guide.

" } }, "configSnapshotDeliveryProperties": { @@ -3193,7 +3254,7 @@ } }, "traits": { - "smithy.api#documentation": "

The status of a specified delivery channel.

\n\t\t

Valid values: Success | Failure\n\t\t

" + "smithy.api#documentation": "

The status of a specified delivery channel.

\n

Valid values: Success | Failure\n

" } }, "com.amazonaws.configservice#DeliveryChannelStatusList": { @@ -3266,7 +3327,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of compliant and noncompliant rules with the\n\t\t\tnumber of resources for compliant and noncompliant rules. Does not display rules that do not have compliance results. \n\t\t\t

\n\t\t \n\t\t\t

The results can return an empty result page, but if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n\t\t
", + "smithy.api#documentation": "

Returns a list of compliant and noncompliant rules with the\n\t\t\tnumber of resources for compliant and noncompliant rules. Does not display rules that do not have compliance results. \n\t\t\t

\n \n

The results can return an empty result page, but if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -3303,6 +3364,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeAggregateComplianceByConfigRulesResponse": { @@ -3320,6 +3384,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeAggregateComplianceByConformancePacks": { @@ -3345,7 +3412,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of the conformance packs and their associated compliance status with the count of compliant and noncompliant Config rules within each \n\t\t\tconformance pack. Also returns the total rule count which includes compliant rules, noncompliant rules, and rules that cannot be evaluated due to insufficient data.

\n\t\t \n

The results can return an empty result page, but if you have a nextToken, the results are displayed on the next page.

\n
", + "smithy.api#documentation": "

Returns a list of the conformance packs and their associated compliance status with the count of compliant and noncompliant Config rules within each \n\t\t\tconformance pack. Also returns the total rule count which includes compliant rules, noncompliant rules, and rules that cannot be evaluated due to insufficient data.

\n \n

The results can return an empty result page, but if you have a nextToken, the results are displayed on the next page.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -3383,6 +3450,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeAggregateComplianceByConformancePacksResponse": { @@ -3400,6 +3470,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeAggregationAuthorizations": { @@ -3447,6 +3520,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeAggregationAuthorizationsResponse": { @@ -3464,6 +3540,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeComplianceByConfigRule": { @@ -3486,7 +3565,7 @@ } ], "traits": { - "smithy.api#documentation": "

Indicates whether the specified Config rules are compliant.\n\t\t\tIf a rule is noncompliant, this action returns the number of Amazon Web Services\n\t\t\tresources that do not comply with the rule.

\n\t\t

A rule is compliant if all of the evaluated resources comply\n\t\t\twith it. It is noncompliant if any of these resources do not\n\t\t\tcomply.

\n\t\t

If Config has no current evaluation results for the rule,\n\t\t\tit returns INSUFFICIENT_DATA. This result might\n\t\t\tindicate one of the following conditions:

\n\t\t
    \n
  • \n\t\t\t\t

    Config has never invoked an evaluation for the\n\t\t\t\t\trule. To check whether it has, use the\n\t\t\t\t\t\tDescribeConfigRuleEvaluationStatus action\n\t\t\t\t\tto get the LastSuccessfulInvocationTime and\n\t\t\t\t\t\tLastFailedInvocationTime.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The rule's Lambda function is failing to send\n\t\t\t\t\tevaluation results to Config. Verify that the role you\n\t\t\t\t\tassigned to your configuration recorder includes the\n\t\t\t\t\t\tconfig:PutEvaluations permission. If the\n\t\t\t\t\trule is a custom rule, verify that the Lambda execution\n\t\t\t\t\trole includes the config:PutEvaluations\n\t\t\t\t\tpermission.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The rule's Lambda function has returned\n\t\t\t\t\t\tNOT_APPLICABLE for all evaluation results.\n\t\t\t\t\tThis can occur if the resources were deleted or removed from\n\t\t\t\t\tthe rule's scope.

    \n\t\t\t
  • \n
", + "smithy.api#documentation": "

Indicates whether the specified Config rules are compliant.\n\t\t\tIf a rule is noncompliant, this action returns the number of Amazon Web Services\n\t\t\tresources that do not comply with the rule.

\n

A rule is compliant if all of the evaluated resources comply\n\t\t\twith it. It is noncompliant if any of these resources do not\n\t\t\tcomply.

\n

If Config has no current evaluation results for the rule,\n\t\t\tit returns INSUFFICIENT_DATA. This result might\n\t\t\tindicate one of the following conditions:

\n
    \n
  • \n

    Config has never invoked an evaluation for the\n\t\t\t\t\trule. To check whether it has, use the\n\t\t\t\t\t\tDescribeConfigRuleEvaluationStatus action\n\t\t\t\t\tto get the LastSuccessfulInvocationTime and\n\t\t\t\t\t\tLastFailedInvocationTime.

    \n
  • \n
  • \n

    The rule's Lambda function is failing to send\n\t\t\t\t\tevaluation results to Config. Verify that the role you\n\t\t\t\t\tassigned to your configuration recorder includes the\n\t\t\t\t\t\tconfig:PutEvaluations permission. If the\n\t\t\t\t\trule is a custom rule, verify that the Lambda execution\n\t\t\t\t\trole includes the config:PutEvaluations\n\t\t\t\t\tpermission.

    \n
  • \n
  • \n

    The rule's Lambda function has returned\n\t\t\t\t\t\tNOT_APPLICABLE for all evaluation results.\n\t\t\t\t\tThis can occur if the resources were deleted or removed from\n\t\t\t\t\tthe rule's scope.

    \n
  • \n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -3506,7 +3585,7 @@ "ComplianceTypes": { "target": "com.amazonaws.configservice#ComplianceTypes", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT and NON_COMPLIANT.

" + "smithy.api#documentation": "

Filters the results by compliance.

" } }, "NextToken": { @@ -3517,7 +3596,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeComplianceByConfigRuleResponse": { @@ -3537,7 +3617,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeComplianceByResource": { @@ -3557,7 +3638,7 @@ } ], "traits": { - "smithy.api#documentation": "

Indicates whether the specified Amazon Web Services resources are compliant. If\n\t\t\ta resource is noncompliant, this action returns the number of Config rules that the resource does not comply with.

\n\t\t

A resource is compliant if it complies with all the Config\n\t\t\trules that evaluate it. It is noncompliant if it does not comply\n\t\t\twith one or more of these rules.

\n\t\t

If Config has no current evaluation results for the\n\t\t\tresource, it returns INSUFFICIENT_DATA. This result\n\t\t\tmight indicate one of the following conditions about the rules that\n\t\t\tevaluate the resource:

\n\t\t
    \n
  • \n\t\t\t\t

    Config has never invoked an evaluation for the\n\t\t\t\t\trule. To check whether it has, use the\n\t\t\t\t\t\tDescribeConfigRuleEvaluationStatus action\n\t\t\t\t\tto get the LastSuccessfulInvocationTime and\n\t\t\t\t\t\tLastFailedInvocationTime.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The rule's Lambda function is failing to send\n\t\t\t\t\tevaluation results to Config. Verify that the role that\n\t\t\t\t\tyou assigned to your configuration recorder includes the\n\t\t\t\t\t\tconfig:PutEvaluations permission. If the\n\t\t\t\t\trule is a custom rule, verify that the Lambda execution\n\t\t\t\t\trole includes the config:PutEvaluations\n\t\t\t\t\tpermission.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The rule's Lambda function has returned\n\t\t\t\t\t\tNOT_APPLICABLE for all evaluation results.\n\t\t\t\t\tThis can occur if the resources were deleted or removed from\n\t\t\t\t\tthe rule's scope.

    \n\t\t\t
  • \n
", + "smithy.api#documentation": "

Indicates whether the specified Amazon Web Services resources are compliant. If\n\t\t\ta resource is noncompliant, this action returns the number of Config rules that the resource does not comply with.

\n

A resource is compliant if it complies with all the Config\n\t\t\trules that evaluate it. It is noncompliant if it does not comply\n\t\t\twith one or more of these rules.

\n

If Config has no current evaluation results for the\n\t\t\tresource, it returns INSUFFICIENT_DATA. This result\n\t\t\tmight indicate one of the following conditions about the rules that\n\t\t\tevaluate the resource:

\n
    \n
  • \n

    Config has never invoked an evaluation for the\n\t\t\t\t\trule. To check whether it has, use the\n\t\t\t\t\t\tDescribeConfigRuleEvaluationStatus action\n\t\t\t\t\tto get the LastSuccessfulInvocationTime and\n\t\t\t\t\t\tLastFailedInvocationTime.

    \n
  • \n
  • \n

    The rule's Lambda function is failing to send\n\t\t\t\t\tevaluation results to Config. Verify that the role that\n\t\t\t\t\tyou assigned to your configuration recorder includes the\n\t\t\t\t\t\tconfig:PutEvaluations permission. If the\n\t\t\t\t\trule is a custom rule, verify that the Lambda execution\n\t\t\t\t\trole includes the config:PutEvaluations\n\t\t\t\t\tpermission.

    \n
  • \n
  • \n

    The rule's Lambda function has returned\n\t\t\t\t\t\tNOT_APPLICABLE for all evaluation results.\n\t\t\t\t\tThis can occur if the resources were deleted or removed from\n\t\t\t\t\tthe rule's scope.

    \n
  • \n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -3584,7 +3665,7 @@ "ComplianceTypes": { "target": "com.amazonaws.configservice#ComplianceTypes", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT, NON_COMPLIANT, and INSUFFICIENT_DATA.

" + "smithy.api#documentation": "

Filters the results by compliance.

" } }, "Limit": { @@ -3602,7 +3683,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeComplianceByResourceResponse": { @@ -3622,7 +3704,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigRuleEvaluationStatus": { @@ -3673,12 +3756,13 @@ "target": "com.amazonaws.configservice#RuleLimit", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The number of rule evaluation results that you want\n\t\t\treturned.

\n\t\t\n\t\t

This parameter is required if the rule limit for your account\n\t\t\tis more than the default of 150 rules.

\n\t\t

For information about requesting a rule limit increase, see\n\t\t\t\tConfig Limits in the Amazon Web Services General\n\t\t\t\tReference Guide.

" + "smithy.api#documentation": "

The number of rule evaluation results that you want\n\t\t\treturned.

\n

This parameter is required if the rule limit for your account\n\t\t\tis more than the default of 150 rules.

\n

For information about requesting a rule limit increase, see\n\t\t\t\tConfig Limits in the Amazon Web Services General\n\t\t\t\tReference Guide.

" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigRuleEvaluationStatusResponse": { @@ -3698,7 +3782,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigRules": { @@ -3740,7 +3825,7 @@ } }, "traits": { - "smithy.api#documentation": "

Returns a filtered list of Detective or Proactive Config rules. By default, if the filter is not defined, this API returns an unfiltered list.

" + "smithy.api#documentation": "

Returns a filtered list of Detective or Proactive Config rules. By default, if the filter is not defined, this API returns an unfiltered list. For more information on Detective or Proactive Config rules,\n\t\t\tsee \n Evaluation Mode\n in the Config Developer Guide.

" } }, "com.amazonaws.configservice#DescribeConfigRulesRequest": { @@ -3761,12 +3846,13 @@ "Filters": { "target": "com.amazonaws.configservice#DescribeConfigRulesFilters", "traits": { - "smithy.api#documentation": "

Returns a list of Detecive or Proactive Config rules. By default, this API returns an unfiltered list.

" + "smithy.api#documentation": "

Returns a list of Detective or Proactive Config rules. By default, this API returns an unfiltered list. For more information on Detective or Proactive Config rules,\n\t\t\tsee \n Evaluation Mode\n in the Config Developer Guide.

" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigRulesResponse": { @@ -3786,7 +3872,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigurationAggregatorSourcesStatus": { @@ -3834,7 +3921,7 @@ "UpdateStatus": { "target": "com.amazonaws.configservice#AggregatedSourceStatusTypeList", "traits": { - "smithy.api#documentation": "

Filters the status type.

\n\t\t
    \n
  • \n\t\t\t\t

    Valid value FAILED indicates errors while moving\n\t\t\t\t\tdata.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Valid value SUCCEEDED indicates the data was\n\t\t\t\t\tsuccessfully moved.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Valid value OUTDATED indicates the data is not the most\n\t\t\t\t\trecent.

    \n\t\t\t
  • \n
" + "smithy.api#documentation": "

Filters the status type.

\n
    \n
  • \n

    Valid value FAILED indicates errors while moving\n\t\t\t\t\tdata.

    \n
  • \n
  • \n

    Valid value SUCCEEDED indicates the data was\n\t\t\t\t\tsuccessfully moved.

    \n
  • \n
  • \n

    Valid value OUTDATED indicates the data is not the most\n\t\t\t\t\trecent.

    \n
  • \n
" } }, "NextToken": { @@ -3850,6 +3937,9 @@ "smithy.api#documentation": "

The maximum number of AggregatorSourceStatus returned on each\n\t\t\tpage. The default is maximum. If you specify 0, Config uses the\n\t\t\tdefault.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigurationAggregatorSourcesStatusResponse": { @@ -3867,6 +3957,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigurationAggregators": { @@ -3923,6 +4016,9 @@ "smithy.api#documentation": "

The maximum number of configuration aggregators returned on\n\t\t\teach page. The default is maximum. If you specify 0, Config uses\n\t\t\tthe default.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigurationAggregatorsResponse": { @@ -3940,6 +4036,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigurationRecorderStatus": { @@ -3956,7 +4055,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the current status of the specified configuration\n\t\t\trecorder. If a configuration recorder is not specified, this action\n\t\t\treturns the status of all configuration recorders associated with\n\t\t\tthe account.

\n\t\t \n\t\t\t

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n\t\t
" + "smithy.api#documentation": "

Returns the current status of the specified configuration\n\t\t\trecorder as well as the status of the last recording event for the recorder. If a configuration recorder is not specified, this action\n\t\t\treturns the status of all configuration recorders associated with\n\t\t\tthe account.

\n \n

>You can specify only one configuration recorder for each Amazon Web Services Region for each account.\n\t\t\t\tFor a detailed status of recording events over time, add your Config events to Amazon CloudWatch metrics and use CloudWatch metrics.

\n
" } }, "com.amazonaws.configservice#DescribeConfigurationRecorderStatusRequest": { @@ -3970,7 +4069,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DescribeConfigurationRecorderStatus\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the DescribeConfigurationRecorderStatus\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigurationRecorderStatusResponse": { @@ -3984,7 +4084,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DescribeConfigurationRecorderStatus action, in JSON\n\t\t\tformat.

" + "smithy.api#documentation": "

The output for the DescribeConfigurationRecorderStatus action, in JSON\n\t\t\tformat.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigurationRecorders": { @@ -4001,7 +4102,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the details for the specified configuration recorders.\n\t\t\tIf the configuration recorder is not specified, this action returns\n\t\t\tthe details for all configuration recorders associated with the\n\t\t\taccount.

\n\t\t \n\t\t\t

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n\t\t
" + "smithy.api#documentation": "

Returns the details for the specified configuration recorders.\n\t\t\tIf the configuration recorder is not specified, this action returns\n\t\t\tthe details for all configuration recorders associated with the\n\t\t\taccount.

\n \n

You can specify only one configuration recorder for each Amazon Web Services Region for each account.

\n
" } }, "com.amazonaws.configservice#DescribeConfigurationRecordersRequest": { @@ -4015,7 +4116,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DescribeConfigurationRecorders action.

" + "smithy.api#documentation": "

The input for the DescribeConfigurationRecorders action.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigurationRecordersResponse": { @@ -4029,7 +4131,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DescribeConfigurationRecorders action.

" + "smithy.api#documentation": "

The output for the DescribeConfigurationRecorders action.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConformancePackCompliance": { @@ -4058,7 +4161,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns compliance details for each rule in that conformance pack.

\n\t\t \n

You must provide exact rule names.

\n
", + "smithy.api#documentation": "

Returns compliance details for each rule in that conformance pack.

\n \n

You must provide exact rule names.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4105,6 +4208,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConformancePackComplianceResponse": { @@ -4130,6 +4236,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConformancePackStatus": { @@ -4152,7 +4261,7 @@ } ], "traits": { - "smithy.api#documentation": "

Provides one or more conformance packs deployment status.

\n\t\t \n

If there are no conformance packs then you will see an empty result.

\n
", + "smithy.api#documentation": "

Provides one or more conformance packs deployment status.

\n \n

If there are no conformance packs then you will see an empty result.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4183,6 +4292,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConformancePackStatusResponse": { @@ -4200,6 +4312,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConformancePacks": { @@ -4256,6 +4371,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConformancePacksResponse": { @@ -4273,6 +4391,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeDeliveryChannelStatus": { @@ -4289,7 +4410,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the current status of the specified delivery channel.\n\t\t\tIf a delivery channel is not specified, this action returns the\n\t\t\tcurrent status of all delivery channels associated with the\n\t\t\taccount.

\n\t\t \n\t\t\t

Currently, you can specify only one delivery channel per\n\t\t\t\tregion in your account.

\n\t\t
" + "smithy.api#documentation": "

Returns the current status of the specified delivery channel.\n\t\t\tIf a delivery channel is not specified, this action returns the\n\t\t\tcurrent status of all delivery channels associated with the\n\t\t\taccount.

\n \n

Currently, you can specify only one delivery channel per\n\t\t\t\tregion in your account.

\n
" } }, "com.amazonaws.configservice#DescribeDeliveryChannelStatusRequest": { @@ -4303,7 +4424,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DeliveryChannelStatus\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the DeliveryChannelStatus\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeDeliveryChannelStatusResponse": { @@ -4317,7 +4439,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DescribeDeliveryChannelStatus action.

" + "smithy.api#documentation": "

The output for the DescribeDeliveryChannelStatus action.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeDeliveryChannels": { @@ -4334,7 +4457,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns details about the specified delivery channel. If a\n\t\t\tdelivery channel is not specified, this action returns the details\n\t\t\tof all delivery channels associated with the account.

\n\t\t \n\t\t\t

Currently, you can specify only one delivery channel per\n\t\t\t\tregion in your account.

\n\t\t
" + "smithy.api#documentation": "

Returns details about the specified delivery channel. If a\n\t\t\tdelivery channel is not specified, this action returns the details\n\t\t\tof all delivery channels associated with the account.

\n \n

Currently, you can specify only one delivery channel per\n\t\t\t\tregion in your account.

\n
" } }, "com.amazonaws.configservice#DescribeDeliveryChannelsRequest": { @@ -4348,7 +4471,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DescribeDeliveryChannels\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the DescribeDeliveryChannels\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeDeliveryChannelsResponse": { @@ -4362,7 +4486,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DescribeDeliveryChannels\n\t\t\taction.

" + "smithy.api#documentation": "

The output for the DescribeDeliveryChannels\n\t\t\taction.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeOrganizationConfigRuleStatuses": { @@ -4388,7 +4513,7 @@ } ], "traits": { - "smithy.api#documentation": "

Provides organization Config rule deployment status for an organization.

\n\t\t\n\t\t \n

The status is not considered successful until organization Config rule is successfully deployed in all the member \n\t\t\taccounts with an exception of excluded accounts.

\n\t\t\t

When you specify the limit and the next token, you receive a paginated response.\n\t\t\tLimit and next token are not applicable if you specify organization Config rule names. \n\t\t\tIt is only applicable, when you request all the organization Config rules.

\n\t\t\t
", + "smithy.api#documentation": "

Provides organization Config rule deployment status for an organization.

\n \n

The status is not considered successful until organization Config rule is successfully deployed in all the member \n\t\t\taccounts with an exception of excluded accounts.

\n

When you specify the limit and the next token, you receive a paginated response.\n\t\t\tLimit and next token are not applicable if you specify organization Config rule names. \n\t\t\tIt is only applicable, when you request all the organization Config rules.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4419,6 +4544,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeOrganizationConfigRuleStatusesResponse": { @@ -4436,6 +4564,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeOrganizationConfigRules": { @@ -4461,7 +4592,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of organization Config rules.

\n\t\t\t\n\t\t \n

When you specify the limit and the next token, you receive a paginated response.

\n\t\t\t

Limit and next token are not applicable if you specify organization Config rule names. \n\t\t\tIt is only applicable, when you request all the organization Config rules.

\n\t\t\t\n\t\t\t

\n For accounts within an organzation\n

\n\t\t\t\n\t\t\t

If you deploy an organizational rule or conformance pack in an organization\n\t\t\t\tadministrator account, and then establish a delegated administrator and deploy an\n\t\t\t\torganizational rule or conformance pack in the delegated administrator account, you\n\t\t\t\twon't be able to see the organizational rule or conformance pack in the organization\n\t\t\t\tadministrator account from the delegated administrator account or see the organizational\n\t\t\t\trule or conformance pack in the delegated administrator account from organization\n\t\t\t\tadministrator account. The DescribeOrganizationConfigRules and \n\t\t\t\tDescribeOrganizationConformancePacks APIs can only see and interact with\n\t\t\t\tthe organization-related resource that were deployed from within the account calling\n\t\t\t\tthose APIs.

\n\t\t
", + "smithy.api#documentation": "

Returns a list of organization Config rules.

\n \n

When you specify the limit and the next token, you receive a paginated response.

\n

Limit and next token are not applicable if you specify organization Config rule names. \n\t\t\tIt is only applicable, when you request all the organization Config rules.

\n

\n For accounts within an organzation\n

\n

If you deploy an organizational rule or conformance pack in an organization\n\t\t\t\tadministrator account, and then establish a delegated administrator and deploy an\n\t\t\t\torganizational rule or conformance pack in the delegated administrator account, you\n\t\t\t\twon't be able to see the organizational rule or conformance pack in the organization\n\t\t\t\tadministrator account from the delegated administrator account or see the organizational\n\t\t\t\trule or conformance pack in the delegated administrator account from organization\n\t\t\t\tadministrator account. The DescribeOrganizationConfigRules and \n\t\t\t\tDescribeOrganizationConformancePacks APIs can only see and interact with\n\t\t\t\tthe organization-related resource that were deployed from within the account calling\n\t\t\t\tthose APIs.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4492,6 +4623,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeOrganizationConfigRulesResponse": { @@ -4509,6 +4643,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeOrganizationConformancePackStatuses": { @@ -4534,7 +4671,7 @@ } ], "traits": { - "smithy.api#documentation": "

Provides organization conformance pack deployment status for an organization.

\n\t\t \n\t\t\t

The status is not considered successful until organization conformance pack is successfully \n\t\t\t\tdeployed in all the member accounts with an exception of excluded accounts.

\n\t\t\t

When you specify the limit and the next token, you receive a paginated response. \n\t\t\t\tLimit and next token are not applicable if you specify organization conformance pack names. \n\t\t\t\tThey are only applicable, when you request all the organization conformance packs.

\n
", + "smithy.api#documentation": "

Provides organization conformance pack deployment status for an organization.

\n \n

The status is not considered successful until organization conformance pack is successfully \n\t\t\t\tdeployed in all the member accounts with an exception of excluded accounts.

\n

When you specify the limit and the next token, you receive a paginated response. \n\t\t\t\tLimit and next token are not applicable if you specify organization conformance pack names. \n\t\t\t\tThey are only applicable, when you request all the organization conformance packs.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4565,6 +4702,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeOrganizationConformancePackStatusesResponse": { @@ -4582,6 +4722,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeOrganizationConformancePacks": { @@ -4607,7 +4750,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of organization conformance packs.

\n\t\t \n

When you specify the limit and the next token, you receive a paginated response.

\n\t\t\t

Limit and next token are not applicable if you specify organization conformance packs names. They are only applicable,\n\t\t\twhen you request all the organization conformance packs.

\n\t\t\n\t\t\t

\n For accounts within an organzation\n

\n\t\t\t\t\t\t\n\t\t\t

If you deploy an organizational rule or conformance pack in an organization\n\t\t\t\tadministrator account, and then establish a delegated administrator and deploy an\n\t\t\t\torganizational rule or conformance pack in the delegated administrator account, you\n\t\t\t\twon't be able to see the organizational rule or conformance pack in the organization\n\t\t\t\tadministrator account from the delegated administrator account or see the organizational\n\t\t\t\trule or conformance pack in the delegated administrator account from organization\n\t\t\t\tadministrator account. The DescribeOrganizationConfigRules and \n\t\t\t\tDescribeOrganizationConformancePacks APIs can only see and interact with\n\t\t\t\tthe organization-related resource that were deployed from within the account calling\n\t\t\t\tthose APIs.

\n\t\t
", + "smithy.api#documentation": "

Returns a list of organization conformance packs.

\n \n

When you specify the limit and the next token, you receive a paginated response.

\n

Limit and next token are not applicable if you specify organization conformance packs names. They are only applicable,\n\t\t\twhen you request all the organization conformance packs.

\n

\n For accounts within an organzation\n

\n

If you deploy an organizational rule or conformance pack in an organization\n\t\t\t\tadministrator account, and then establish a delegated administrator and deploy an\n\t\t\t\torganizational rule or conformance pack in the delegated administrator account, you\n\t\t\t\twon't be able to see the organizational rule or conformance pack in the organization\n\t\t\t\tadministrator account from the delegated administrator account or see the organizational\n\t\t\t\trule or conformance pack in the delegated administrator account from organization\n\t\t\t\tadministrator account. The DescribeOrganizationConfigRules and \n\t\t\t\tDescribeOrganizationConformancePacks APIs can only see and interact with\n\t\t\t\tthe organization-related resource that were deployed from within the account calling\n\t\t\t\tthose APIs.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4638,6 +4781,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a\n\t\t\tpaginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeOrganizationConformancePacksResponse": { @@ -4655,6 +4801,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a\n\t\t\tpaginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribePendingAggregationRequests": { @@ -4712,6 +4861,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribePendingAggregationRequestsResponse": { @@ -4729,6 +4881,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeRemediationConfigurations": { @@ -4753,6 +4908,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeRemediationConfigurationsResponse": { @@ -4764,6 +4922,9 @@ "smithy.api#documentation": "

Returns a remediation configuration object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeRemediationExceptions": { @@ -4783,7 +4944,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the details of one or more remediation exceptions. A detailed view of a remediation exception for a set of resources that includes an explanation of an exception and the time when the exception will be deleted. \n\t\t\tWhen you specify the limit and the next token, you receive a paginated response.

\n\t\t \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n\t\t\t

When you specify the limit and the next token, you receive a paginated response.

\n\t\t\t

Limit and next token are not applicable if you request resources in batch. It is only applicable, when you request all resources.

\n
", + "smithy.api#documentation": "

Returns the details of one or more remediation exceptions. A detailed view of a remediation exception for a set of resources that includes an explanation of an exception and the time when the exception will be deleted. \n\t\t\tWhen you specify the limit and the next token, you receive a paginated response.

\n \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n

When you specify the limit and the next token, you receive a paginated response.

\n

Limit and next token are not applicable if you request resources in batch. It is only applicable, when you request all resources.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4820,6 +4981,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeRemediationExceptionsResponse": { @@ -4837,6 +5001,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeRemediationExecutionStatus": { @@ -4897,6 +5064,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeRemediationExecutionStatusResponse": { @@ -4914,6 +5084,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeRetentionConfigurations": { @@ -4936,7 +5109,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the details of one or more retention configurations. If\n\t\t\tthe retention configuration name is not specified, this action\n\t\t\treturns the details for all the retention configurations for that\n\t\t\taccount.

\n\t\t \n\t\t\t

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n\t\t
", + "smithy.api#documentation": "

Returns the details of one or more retention configurations. If\n\t\t\tthe retention configuration name is not specified, this action\n\t\t\treturns the details for all the retention configurations for that\n\t\t\taccount.

\n \n

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4950,7 +5123,7 @@ "RetentionConfigurationNames": { "target": "com.amazonaws.configservice#RetentionConfigurationNameList", "traits": { - "smithy.api#documentation": "

A list of names of retention configurations for which you want\n\t\t\tdetails. If you do not specify a name, Config returns details\n\t\t\tfor all the retention configurations for that account.

\n\t\t \n\t\t\t

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n\t\t
" + "smithy.api#documentation": "

A list of names of retention configurations for which you want\n\t\t\tdetails. If you do not specify a name, Config returns details\n\t\t\tfor all the retention configurations for that account.

\n \n

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n
" } }, "NextToken": { @@ -4959,6 +5132,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page\n\t\t\tthat you use to get the next page of results in a paginated\n\t\t\tresponse.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeRetentionConfigurationsResponse": { @@ -4976,6 +5152,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page\n\t\t\tthat you use to get the next page of results in a paginated\n\t\t\tresponse.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DiscoveredResourceIdentifierList": { @@ -5019,7 +5198,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

Indicates whether the Amazon Web Services resource complies with the Config\n\t\t\trule that it was evaluated against.

\n\t\t

For the Evaluation data type, Config supports\n\t\t\tonly the COMPLIANT, NON_COMPLIANT, and\n\t\t\t\tNOT_APPLICABLE values. Config does not support\n\t\t\tthe INSUFFICIENT_DATA value for this data\n\t\t\ttype.

\n\t\t

Similarly, Config does not accept\n\t\t\t\tINSUFFICIENT_DATA as the value for\n\t\t\t\tComplianceType from a PutEvaluations\n\t\t\trequest. For example, an Lambda function for a custom Config\n\t\t\trule cannot pass an INSUFFICIENT_DATA value to Config.

", + "smithy.api#documentation": "

Indicates whether the Amazon Web Services resource complies with the Config\n\t\t\trule that it was evaluated against.

\n

For the Evaluation data type, Config supports\n\t\t\tonly the COMPLIANT, NON_COMPLIANT, and\n\t\t\t\tNOT_APPLICABLE values. Config does not support\n\t\t\tthe INSUFFICIENT_DATA value for this data\n\t\t\ttype.

\n

Similarly, Config does not accept\n\t\t\t\tINSUFFICIENT_DATA as the value for\n\t\t\t\tComplianceType from a PutEvaluations\n\t\t\trequest. For example, an Lambda function for a custom Config\n\t\t\trule cannot pass an INSUFFICIENT_DATA value to Config.

", "smithy.api#required": {} } }, @@ -5113,7 +5292,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

Indicates whether the Amazon Web Services resource complies with the Config\n\t\t\trule that evaluated it.

\n\t\t

For the EvaluationResult data type, Config\n\t\t\tsupports only the COMPLIANT,\n\t\t\tNON_COMPLIANT, and NOT_APPLICABLE values.\n\t\t\tConfig does not support the INSUFFICIENT_DATA value\n\t\t\tfor the EvaluationResult data type.

" + "smithy.api#documentation": "

Indicates whether the Amazon Web Services resource complies with the Config\n\t\t\trule that evaluated it.

\n

For the EvaluationResult data type, Config\n\t\t\tsupports only the COMPLIANT,\n\t\t\tNON_COMPLIANT, and NOT_APPLICABLE values.\n\t\t\tConfig does not support the INSUFFICIENT_DATA value\n\t\t\tfor the EvaluationResult data type.

" } }, "ResultRecordedTime": { @@ -5275,6 +5454,20 @@ } } }, + "com.amazonaws.configservice#ExclusionByResourceTypes": { + "type": "structure", + "members": { + "resourceTypes": { + "target": "com.amazonaws.configservice#ResourceTypeList", + "traits": { + "smithy.api#documentation": "

A comma-separated list of resource types to exclude from recording by the configuration\n\t\t\trecorder.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies whether the configuration recorder excludes resource types from being recorded.\n\t\t\tUse the resourceTypes field to enter a comma-separated list of resource types to exclude as exemptions.

" + } + }, "com.amazonaws.configservice#ExecutionControls": { "type": "structure", "members": { @@ -5464,7 +5657,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the evaluation results for the specified Config\n\t\t\trule for a specific resource in a rule. The results indicate which\n\t\t\tAmazon Web Services resources were evaluated by the rule, when each resource was\n\t\t\tlast evaluated, and whether each resource complies with the rule.

\n\t\t \n\t\t\t

The results can return an empty result page. But if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n\t\t
", + "smithy.api#documentation": "

Returns the evaluation results for the specified Config\n\t\t\trule for a specific resource in a rule. The results indicate which\n\t\t\tAmazon Web Services resources were evaluated by the rule, when each resource was\n\t\t\tlast evaluated, and whether each resource complies with the rule.

\n \n

The results can return an empty result page. But if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -5507,7 +5700,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

The resource compliance status.

\n\t\t \n\t\t\t

For the\n\t\t\t\t\tGetAggregateComplianceDetailsByConfigRuleRequest\n\t\t\t\tdata type, Config supports only the COMPLIANT\n\t\t\t\tand NON_COMPLIANT. Config does not support the\n\t\t\t\t\tNOT_APPLICABLE and\n\t\t\t\t\tINSUFFICIENT_DATA values.

\n\t\t
" + "smithy.api#documentation": "

The resource compliance status.

\n \n

For the\n\t\t\t\t\tGetAggregateComplianceDetailsByConfigRuleRequest\n\t\t\t\tdata type, Config supports only the COMPLIANT\n\t\t\t\tand NON_COMPLIANT. Config does not support the\n\t\t\t\t\tNOT_APPLICABLE and\n\t\t\t\t\tINSUFFICIENT_DATA values.

\n
" } }, "Limit": { @@ -5523,6 +5716,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateComplianceDetailsByConfigRuleResponse": { @@ -5540,6 +5736,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetAggregateConfigRuleComplianceSummary": { @@ -5565,7 +5764,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the number of compliant and noncompliant rules for one\n\t\t\tor more accounts and regions in an aggregator.

\n\t\t \n\t\t\t

The results can return an empty result page, but if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n\t\t
", + "smithy.api#documentation": "

Returns the number of compliant and noncompliant rules for one\n\t\t\tor more accounts and regions in an aggregator.

\n \n

The results can return an empty result page, but if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -5608,6 +5807,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateConfigRuleComplianceSummaryResponse": { @@ -5631,6 +5833,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetAggregateConformancePackComplianceSummary": { @@ -5656,7 +5861,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the count of compliant and noncompliant conformance packs across all Amazon Web Services accounts and Amazon Web Services Regions in an aggregator. You can filter based on Amazon Web Services account ID or Amazon Web Services Region.

\n\t\t \n

The results can return an empty result page, but if you have a nextToken, the results are displayed on the next page.

\n
", + "smithy.api#documentation": "

Returns the count of compliant and noncompliant conformance packs across all Amazon Web Services accounts and Amazon Web Services Regions in an aggregator. You can filter based on Amazon Web Services account ID or Amazon Web Services Region.

\n \n

The results can return an empty result page, but if you have a nextToken, the results are displayed on the next page.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -5699,6 +5904,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateConformancePackComplianceSummaryResponse": { @@ -5722,6 +5930,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetAggregateDiscoveredResourceCounts": { @@ -5747,7 +5958,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the resource counts across accounts and regions that are present in your Config aggregator. You can request the resource counts by providing filters and GroupByKey.

\n\t\t

For example, if the input contains accountID 12345678910 and region us-east-1 in filters, the API returns the count of resources in account ID 12345678910 and region us-east-1.\n\t\t\tIf the input contains ACCOUNT_ID as a GroupByKey, the API returns resource counts for all source accounts that are present in your aggregator.

", + "smithy.api#documentation": "

Returns the resource counts across accounts and regions that are present in your Config aggregator. You can request the resource counts by providing filters and GroupByKey.

\n

For example, if the input contains accountID 12345678910 and region us-east-1 in filters, the API returns the count of resources in account ID 12345678910 and region us-east-1.\n\t\t\tIf the input contains ACCOUNT_ID as a GroupByKey, the API returns resource counts for all source accounts that are present in your aggregator.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -5790,6 +6001,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateDiscoveredResourceCountsResponse": { @@ -5821,6 +6035,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetAggregateResourceConfig": { @@ -5866,6 +6083,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateResourceConfigResponse": { @@ -5877,6 +6097,9 @@ "smithy.api#documentation": "

Returns a ConfigurationItem object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetComplianceDetailsByConfigRule": { @@ -5921,7 +6144,7 @@ "ComplianceTypes": { "target": "com.amazonaws.configservice#ComplianceTypes", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT,\n\t\t\t\tNON_COMPLIANT, and\n\t\t\tNOT_APPLICABLE.

" + "smithy.api#documentation": "

Filters the results by compliance.

\n

\n INSUFFICIENT_DATA is a valid ComplianceType that is returned when an Config rule cannot be evaluated. However, INSUFFICIENT_DATA cannot be used as a ComplianceType for filtering results.

" } }, "Limit": { @@ -5939,7 +6162,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetComplianceDetailsByConfigRuleResponse": { @@ -5959,7 +6183,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetComplianceDetailsByResource": { @@ -6002,7 +6227,7 @@ "ComplianceTypes": { "target": "com.amazonaws.configservice#ComplianceTypes", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT,\n\t\t\t\tNON_COMPLIANT, and\n\t\t\tNOT_APPLICABLE.

" + "smithy.api#documentation": "

Filters the results by compliance.

\n

\n INSUFFICIENT_DATA is a valid ComplianceType that is returned when an Config rule cannot be evaluated. However, INSUFFICIENT_DATA cannot be used as a ComplianceType for filtering results.

" } }, "NextToken": { @@ -6014,12 +6239,13 @@ "ResourceEvaluationId": { "target": "com.amazonaws.configservice#ResourceEvaluationId", "traits": { - "smithy.api#documentation": "

The unique ID of Amazon Web Services resource execution for which you want to retrieve evaluation results.

\n\t\t \n

You need to only provide either a ResourceEvaluationID or a ResourceID and ResourceType.

\n
" + "smithy.api#documentation": "

The unique ID of Amazon Web Services resource execution for which you want to retrieve evaluation results.

\n \n

You need to only provide either a ResourceEvaluationID or a ResourceID and ResourceType.

\n
" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetComplianceDetailsByResourceResponse": { @@ -6039,7 +6265,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetComplianceSummaryByConfigRule": { @@ -6065,7 +6292,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetComplianceSummaryByResourceType": { @@ -6091,12 +6319,13 @@ "ResourceTypes": { "target": "com.amazonaws.configservice#ResourceTypes", "traits": { - "smithy.api#documentation": "

Specify one or more resource types to get the number of\n\t\t\tresources that are compliant and the number that are noncompliant\n\t\t\tfor each resource type.

\n\t\t

For this request, you can specify an Amazon Web Services resource type such as\n\t\t\t\tAWS::EC2::Instance. You can specify that the\n\t\t\tresource type is an Amazon Web Services account by specifying\n\t\t\t\tAWS::::Account.

" + "smithy.api#documentation": "

Specify one or more resource types to get the number of\n\t\t\tresources that are compliant and the number that are noncompliant\n\t\t\tfor each resource type.

\n

For this request, you can specify an Amazon Web Services resource type such as\n\t\t\t\tAWS::EC2::Instance. You can specify that the\n\t\t\tresource type is an Amazon Web Services account by specifying\n\t\t\t\tAWS::::Account.

" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetComplianceSummaryByResourceTypeResponse": { @@ -6110,7 +6339,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetConformancePackComplianceDetails": { @@ -6186,6 +6416,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetConformancePackComplianceDetailsResponse": { @@ -6210,6 +6443,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetConformancePackComplianceSummary": { @@ -6264,6 +6500,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetConformancePackComplianceSummaryResponse": { @@ -6281,6 +6520,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetCustomRulePolicy": { @@ -6309,6 +6551,9 @@ "smithy.api#documentation": "

The name of your Config Custom Policy rule.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetCustomRulePolicyResponse": { @@ -6320,6 +6565,9 @@ "smithy.api#documentation": "

The policy definition containing the logic for your Config Custom Policy rule.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetDiscoveredResourceCounts": { @@ -6342,7 +6590,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the resource types, the number of each resource type,\n\t\t\tand the total number of resources that Config is recording in\n\t\t\tthis region for your Amazon Web Services account.

\n\t\t

\n Example\n

\n
    \n
  1. \n\t\t\t\t

    Config is recording three resource types in the US\n\t\t\t\t\tEast (Ohio) Region for your account: 25 EC2 instances, 20\n\t\t\t\t\tIAM users, and 15 S3 buckets.

    \n\t\t\t
  2. \n
  3. \n\t\t\t\t

    You make a call to the\n\t\t\t\t\t\tGetDiscoveredResourceCounts action and\n\t\t\t\t\tspecify that you want all resource types.

    \n\t\t\t
  4. \n
  5. \n\t\t\t\t

    Config returns the following:

    \n\n\t\t\t\t
      \n
    • \n\t\t\t\t\t\t

      The resource types (EC2 instances, IAM users,\n\t\t\t\t\t\t\tand S3 buckets).

      \n\t\t\t\t\t
    • \n
    • \n\t\t\t\t\t\t

      The number of each resource type (25, 20, and\n\t\t\t\t\t\t\t15).

      \n\t\t\t\t\t
    • \n
    • \n\t\t\t\t\t\t

      The total number of all resources\n\t\t\t\t\t\t\t(60).

      \n\t\t\t\t\t
    • \n
    \n\n\t\t\t
  6. \n
\n\n\t\t

The response is paginated. By default, Config lists 100\n\t\t\t\tResourceCount objects on each page. You can\n\t\t\tcustomize this number with the limit parameter. The\n\t\t\tresponse includes a nextToken string. To get the next\n\t\t\tpage of results, run the request again and specify the string for\n\t\t\tthe nextToken parameter.

\n\n\t\t \n\t\t\t

If you make a call to the GetDiscoveredResourceCounts action, you might\n\t\t\t\tnot immediately receive resource counts in the following\n\t\t\t\tsituations:

\n\n\t\t\t
    \n
  • \n\t\t\t\t\t

    You are a new Config customer.

    \n\t\t\t\t
  • \n
  • \n\t\t\t\t\t

    You just enabled resource recording.

    \n\t\t\t\t
  • \n
\n\n\t\t\t

It might take a few minutes for Config to record and\n\t\t\t\tcount your resources. Wait a few minutes and then retry the\n\t\t\t\t\tGetDiscoveredResourceCounts action.\n\t\t\t

\n\t\t
", + "smithy.api#documentation": "

Returns the resource types, the number of each resource type,\n\t\t\tand the total number of resources that Config is recording in\n\t\t\tthis region for your Amazon Web Services account.

\n

\n Example\n

\n
    \n
  1. \n

    Config is recording three resource types in the US\n\t\t\t\t\tEast (Ohio) Region for your account: 25 EC2 instances, 20\n\t\t\t\t\tIAM users, and 15 S3 buckets.

    \n
  2. \n
  3. \n

    You make a call to the\n\t\t\t\t\t\tGetDiscoveredResourceCounts action and\n\t\t\t\t\tspecify that you want all resource types.

    \n
  4. \n
  5. \n

    Config returns the following:

    \n
      \n
    • \n

      The resource types (EC2 instances, IAM users,\n\t\t\t\t\t\t\tand S3 buckets).

      \n
    • \n
    • \n

      The number of each resource type (25, 20, and\n\t\t\t\t\t\t\t15).

      \n
    • \n
    • \n

      The total number of all resources\n\t\t\t\t\t\t\t(60).

      \n
    • \n
    \n
  6. \n
\n

The response is paginated. By default, Config lists 100\n\t\t\t\tResourceCount objects on each page. You can\n\t\t\tcustomize this number with the limit parameter. The\n\t\t\tresponse includes a nextToken string. To get the next\n\t\t\tpage of results, run the request again and specify the string for\n\t\t\tthe nextToken parameter.

\n \n

If you make a call to the GetDiscoveredResourceCounts action, you might\n\t\t\t\tnot immediately receive resource counts in the following\n\t\t\t\tsituations:

\n
    \n
  • \n

    You are a new Config customer.

    \n
  • \n
  • \n

    You just enabled resource recording.

    \n
  • \n
\n

It might take a few minutes for Config to record and\n\t\t\t\tcount your resources. Wait a few minutes and then retry the\n\t\t\t\t\tGetDiscoveredResourceCounts action.\n\t\t\t

\n
", "smithy.api#paginated": { "inputToken": "nextToken", "outputToken": "nextToken", @@ -6356,7 +6604,7 @@ "resourceTypes": { "target": "com.amazonaws.configservice#ResourceTypes", "traits": { - "smithy.api#documentation": "

The comma-separated list that specifies the resource types that\n\t\t\tyou want Config to return (for example,\n\t\t\t\t\"AWS::EC2::Instance\",\n\t\t\t\"AWS::IAM::User\").

\n\n\t\t

If a value for resourceTypes is not specified, Config returns all resource types that Config is recording in\n\t\t\tthe region for your account.

\n\t\t \n\t\t\t

If the configuration recorder is turned off, Config\n\t\t\t\treturns an empty list of ResourceCount\n\t\t\t\tobjects. If the configuration recorder is not recording a\n\t\t\t\tspecific resource type (for example, S3 buckets), that resource\n\t\t\t\ttype is not returned in the list of ResourceCount objects.

\n\t\t
" + "smithy.api#documentation": "

The comma-separated list that specifies the resource types that\n\t\t\tyou want Config to return (for example,\n\t\t\t\t\"AWS::EC2::Instance\",\n\t\t\t\"AWS::IAM::User\").

\n

If a value for resourceTypes is not specified, Config returns all resource types that Config is recording in\n\t\t\tthe region for your account.

\n \n

If the configuration recorder is turned off, Config\n\t\t\t\treturns an empty list of ResourceCount\n\t\t\t\tobjects. If the configuration recorder is not recording a\n\t\t\t\tspecific resource type (for example, S3 buckets), that resource\n\t\t\t\ttype is not returned in the list of ResourceCount objects.

\n
" } }, "limit": { @@ -6372,6 +6620,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page\n\t\t\tthat you use to get the next page of results in a paginated\n\t\t\tresponse.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetDiscoveredResourceCountsResponse": { @@ -6381,7 +6632,7 @@ "target": "com.amazonaws.configservice#Long", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The total number of resources that Config is recording in\n\t\t\tthe region for your account. If you specify resource types in the\n\t\t\trequest, Config returns only the total number of resources for\n\t\t\tthose resource types.

\n\n\n\t\t

\n Example\n

\n
    \n
  1. \n\t\t\t\t

    Config is recording three resource types in the US\n\t\t\t\t\tEast (Ohio) Region for your account: 25 EC2 instances, 20\n\t\t\t\t\tIAM users, and 15 S3 buckets, for a total of 60\n\t\t\t\t\tresources.

    \n\t\t\t
  2. \n
  3. \n\t\t\t\t

    You make a call to the\n\t\t\t\t\t\tGetDiscoveredResourceCounts action and\n\t\t\t\t\tspecify the resource type,\n\t\t\t\t\t\t\"AWS::EC2::Instances\", in the\n\t\t\t\t\trequest.

    \n\t\t\t
  4. \n
  5. \n\t\t\t\t

    Config returns 25 for\n\t\t\t\t\t\ttotalDiscoveredResources.

    \n\t\t\t
  6. \n
" + "smithy.api#documentation": "

The total number of resources that Config is recording in\n\t\t\tthe region for your account. If you specify resource types in the\n\t\t\trequest, Config returns only the total number of resources for\n\t\t\tthose resource types.

\n

\n Example\n

\n
    \n
  1. \n

    Config is recording three resource types in the US\n\t\t\t\t\tEast (Ohio) Region for your account: 25 EC2 instances, 20\n\t\t\t\t\tIAM users, and 15 S3 buckets, for a total of 60\n\t\t\t\t\tresources.

    \n
  2. \n
  3. \n

    You make a call to the\n\t\t\t\t\t\tGetDiscoveredResourceCounts action and\n\t\t\t\t\tspecify the resource type,\n\t\t\t\t\t\t\"AWS::EC2::Instances\", in the\n\t\t\t\t\trequest.

    \n
  4. \n
  5. \n

    Config returns 25 for\n\t\t\t\t\t\ttotalDiscoveredResources.

    \n
  6. \n
" } }, "resourceCounts": { @@ -6396,6 +6647,9 @@ "smithy.api#documentation": "

The string that you use in a subsequent request to get the next\n\t\t\tpage of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetOrganizationConfigRuleDetailedStatus": { @@ -6459,6 +6713,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetOrganizationConfigRuleDetailedStatusResponse": { @@ -6476,6 +6733,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetOrganizationConformancePackDetailedStatus": { @@ -6539,6 +6799,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetOrganizationConformancePackDetailedStatusResponse": { @@ -6556,6 +6819,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetOrganizationCustomRulePolicy": { @@ -6588,6 +6854,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetOrganizationCustomRulePolicyResponse": { @@ -6599,6 +6868,9 @@ "smithy.api#documentation": "

The policy definition containing the logic for your organization Config Custom Policy rule.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetResourceConfigHistory": { @@ -6630,7 +6902,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of ConfigurationItems for the specified resource.\n\t\t\tThe list contains details about each state of the resource\n\t\t\tduring the specified time interval. If you specified a retention\n\t\t\tperiod to retain your ConfigurationItems between a\n\t\t\tminimum of 30 days and a maximum of 7 years (2557 days), Config\n\t\t\treturns the ConfigurationItems for the specified\n\t\t\tretention period.

\n\t\t

The response is paginated. By default, Config returns a\n\t\t\tlimit of 10 configuration items per page. You can customize this\n\t\t\tnumber with the limit parameter. The response includes\n\t\t\ta nextToken string. To get the next page of results,\n\t\t\trun the request again and specify the string for the\n\t\t\t\tnextToken parameter.

\n\t\t \n\t\t\t

Each call to the API is limited to span a duration of seven\n\t\t\t\tdays. It is likely that the number of records returned is\n\t\t\t\tsmaller than the specified limit. In such cases,\n\t\t\t\tyou can make another call, using the\n\t\t\t\tnextToken.

\n\t\t
", + "smithy.api#documentation": "

Returns a list of ConfigurationItems for the specified resource.\n\t\t\tThe list contains details about each state of the resource\n\t\t\tduring the specified time interval. If you specified a retention\n\t\t\tperiod to retain your ConfigurationItems between a\n\t\t\tminimum of 30 days and a maximum of 7 years (2557 days), Config\n\t\t\treturns the ConfigurationItems for the specified\n\t\t\tretention period.

\n

The response is paginated. By default, Config returns a\n\t\t\tlimit of 10 configuration items per page. You can customize this\n\t\t\tnumber with the limit parameter. The response includes\n\t\t\ta nextToken string. To get the next page of results,\n\t\t\trun the request again and specify the string for the\n\t\t\t\tnextToken parameter.

\n \n

Each call to the API is limited to span a duration of seven\n\t\t\t\tdays. It is likely that the number of records returned is\n\t\t\t\tsmaller than the specified limit. In such cases,\n\t\t\t\tyou can make another call, using the\n\t\t\t\tnextToken.

\n
", "smithy.api#paginated": { "inputToken": "nextToken", "outputToken": "nextToken", @@ -6689,7 +6961,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the GetResourceConfigHistory\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the GetResourceConfigHistory\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetResourceConfigHistoryResponse": { @@ -6709,7 +6982,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the GetResourceConfigHistory\n\t\t\taction.

" + "smithy.api#documentation": "

The output for the GetResourceConfigHistory\n\t\t\taction.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetResourceEvaluationSummary": { @@ -6726,7 +7000,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a summary of resource evaluation for the specified resource evaluation ID from the proactive rules that were run. \n\t\t\tThe results indicate which evaluation context was used to evaluate the rules, which resource details were evaluated,\n\t\t\tthe evaluation mode that was run, and whether the resource details comply with the configuration of the proactive rules.

" + "smithy.api#documentation": "

Returns a summary of resource evaluation for the specified resource evaluation ID from the proactive rules that were run. \n\t\t\tThe results indicate which evaluation context was used to evaluate the rules, which resource details were evaluated,\n\t\t\tthe evaluation mode that was run, and whether the resource details comply with the configuration of the proactive rules.

\n \n

To see additional information about the evaluation result, such as which rule flagged a resource as NON_COMPLIANT, use the GetComplianceDetailsByResource API.\n\t\t\tFor more information, see the Examples section.

\n
" } }, "com.amazonaws.configservice#GetResourceEvaluationSummaryRequest": { @@ -6739,6 +7013,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetResourceEvaluationSummaryResponse": { @@ -6786,6 +7063,9 @@ "smithy.api#documentation": "

Returns a ResourceDetails object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetStoredQuery": { @@ -6818,6 +7098,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetStoredQueryResponse": { @@ -6829,6 +7112,9 @@ "smithy.api#documentation": "

Returns a StoredQuery object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GroupByAPILimit": { @@ -6915,7 +7201,7 @@ } }, "traits": { - "smithy.api#documentation": "

Indicates one of the following errors:

\n\t\t
    \n
  • \n

    For PutConfigRule, the rule cannot be created because the IAM role assigned to Config lacks permissions to perform the config:Put* action.

    \n
  • \n
  • \n

    For PutConfigRule, the Lambda function cannot be invoked. Check the function ARN, and check the function's permissions.

    \n
  • \n
  • \n

    For PutOrganizationConfigRule, organization Config rule cannot be created because you do not have permissions to call IAM GetRole action or create a service-linked role.

    \n
  • \n
  • \n

    For PutConformancePack and PutOrganizationConformancePack, a conformance pack cannot be created because you do not have the following permissions:

    \n\t\t\t\t
      \n
    • \n

      You do not have permission to call IAM GetRole action or create a service-linked role.

      \n
    • \n
    • \n

      You do not have permission to read Amazon S3 bucket or call SSM:GetDocument.

      \n
    • \n
    \n\t\t\t
  • \n
", + "smithy.api#documentation": "

Indicates one of the following errors:

\n
    \n
  • \n

    For PutConfigRule, the rule cannot be created because the IAM role assigned to Config lacks permissions to perform the config:Put* action.

    \n
  • \n
  • \n

    For PutConfigRule, the Lambda function cannot be invoked. Check the function ARN, and check the function's permissions.

    \n
  • \n
  • \n

    For PutOrganizationConfigRule, organization Config rule cannot be created because you do not have permissions to call IAM GetRole action or create a service-linked role.

    \n
  • \n
  • \n

    For PutConformancePack and PutOrganizationConformancePack, a conformance pack cannot be created because you do not have the following permissions:

    \n
      \n
    • \n

      You do not have permission to call IAM GetRole action or create a service-linked role.

      \n
    • \n
    • \n

      You do not have permission to read Amazon S3 bucket or call SSM:GetDocument.

      \n
    • \n
    \n
  • \n
", "smithy.api#error": "client" } }, @@ -6936,7 +7222,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have provided a configuration recorder name that is not\n\t\t\tvalid.

", + "smithy.api#documentation": "

You have provided a name for the configuration recorder that is not\n\t\t\tvalid.

", "smithy.api#error": "client" } }, @@ -6951,7 +7237,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified delivery channel name is invalid.

", + "smithy.api#documentation": "

The specified delivery channel name is not valid.

", "smithy.api#error": "client" } }, @@ -6996,7 +7282,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified next token is invalid. Specify the\n\t\t\t\tnextToken string that was returned in the previous\n\t\t\tresponse to get the next page of results.

", + "smithy.api#documentation": "

The specified next token is not valid. Specify the\n\t\t\t\tnextToken string that was returned in the previous\n\t\t\tresponse to get the next page of results.

", "smithy.api#error": "client" } }, @@ -7011,7 +7297,7 @@ } }, "traits": { - "smithy.api#documentation": "

One or more of the specified parameters are invalid. Verify\n\t\t\tthat your parameters are valid and try again.

", + "smithy.api#documentation": "

One or more of the specified parameters are not valid. Verify\n\t\t\tthat your parameters are valid and try again.

", "smithy.api#error": "client" } }, @@ -7026,7 +7312,7 @@ } }, "traits": { - "smithy.api#documentation": "

Config throws an exception if the recording group does not contain a valid list of resource types. Invalid values might also be incorrectly formatted.

", + "smithy.api#documentation": "

Indicates one of the following errors:

\n
    \n
  • \n

    You have provided a combination of parameter values that is not valid. For example:

    \n
      \n
    • \n

      Setting the allSupported field of RecordingGroup to true,\n\t\t\t\t\t\tbut providing a non-empty list for the resourceTypesfield of RecordingGroup.

      \n
    • \n
    • \n

      Setting the allSupported field of RecordingGroup to true, but also setting the useOnly field of RecordingStrategy to EXCLUSION_BY_RESOURCE_TYPES.

      \n
    • \n
    \n
  • \n
  • \n

    Every parameter is either null, false, or empty.

    \n
  • \n
  • \n

    You have reached the limit of the number of resource types you can provide for the recording group.

    \n
  • \n
  • \n

    You have provided resource types or a recording strategy that are not valid.

    \n
  • \n
", "smithy.api#error": "client" } }, @@ -7041,7 +7327,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified ResultToken is invalid.

", + "smithy.api#documentation": "

The specified ResultToken is not valid.

", "smithy.api#error": "client" } }, @@ -7056,7 +7342,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have provided a null or empty role ARN.

", + "smithy.api#documentation": "

You have provided a null or empty Amazon Resource Name (ARN) for the IAM role assumed by Config and used by the configuration recorder.

", "smithy.api#error": "client" } }, @@ -7071,7 +7357,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified Amazon S3 key prefix is invalid.

", + "smithy.api#documentation": "

The specified Amazon S3 key prefix is not valid.

", "smithy.api#error": "client" } }, @@ -7086,7 +7372,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified Amazon KMS Key ARN is invalid.

", + "smithy.api#documentation": "

The specified Amazon KMS Key ARN is not valid.

", "smithy.api#error": "client" } }, @@ -7116,7 +7402,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified time range is invalid. The earlier time is not\n\t\t\tchronologically before the later time.

", + "smithy.api#documentation": "

The specified time range is not valid. The earlier time is not\n\t\t\tchronologically before the later time.

", "smithy.api#error": "client" } }, @@ -7162,7 +7448,7 @@ } }, "traits": { - "smithy.api#documentation": "

For StartConfigRulesEvaluation API, this exception\n\t\t\tis thrown if an evaluation is in progress or if you call the StartConfigRulesEvaluation API more than once per\n\t\t\tminute.

\n\t\t

For PutConfigurationAggregator API, this exception\n\t\t\tis thrown if the number of accounts and aggregators exceeds the\n\t\t\tlimit.

", + "smithy.api#documentation": "

For StartConfigRulesEvaluation API, this exception\n\t\t\tis thrown if an evaluation is in progress or if you call the StartConfigRulesEvaluation API more than once per\n\t\t\tminute.

\n

For PutConfigurationAggregator API, this exception\n\t\t\tis thrown if the number of accounts and aggregators exceeds the\n\t\t\tlimit.

", "smithy.api#error": "client" } }, @@ -7189,7 +7475,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a resource type and returns a list of resource identifiers that are aggregated for a specific resource type across accounts and regions. \n\t\t\tA resource identifier includes the resource type, ID, (if available) the custom resource name, source account, and source region. \n\t\t\tYou can narrow the results to include only resources that have specific resource IDs, or a resource name, or source account ID, or source region.

\n\t\t\t

For example, if the input consists of accountID 12345678910 and the region is us-east-1 for resource type AWS::EC2::Instance then the API returns all the EC2 instance identifiers of accountID 12345678910 and region us-east-1.

", + "smithy.api#documentation": "

Accepts a resource type and returns a list of resource identifiers that are aggregated for a specific resource type across accounts and regions. \n\t\t\tA resource identifier includes the resource type, ID, (if available) the custom resource name, source account, and source region. \n\t\t\tYou can narrow the results to include only resources that have specific resource IDs, or a resource name, or source account ID, or source region.

\n

For example, if the input consists of accountID 12345678910 and the region is us-east-1 for resource type AWS::EC2::Instance then the API returns all the EC2 instance identifiers of accountID 12345678910 and region us-east-1.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -7234,6 +7520,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListAggregateDiscoveredResourcesResponse": { @@ -7251,6 +7540,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListConformancePackComplianceScores": { @@ -7273,7 +7565,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of conformance pack compliance scores. \n\t\t\tA compliance score is the percentage of the number of compliant rule-resource combinations in a conformance pack compared to the number of total possible rule-resource combinations in the conformance pack.\n\t\t\tThis metric provides you with a high-level view of the compliance state of your conformance packs. You can use it to identify, investigate, and understand\n\t\t\tthe level of compliance in your conformance packs.

\n\t\t \n

Conformance packs with no evaluation results will have a compliance score of INSUFFICIENT_DATA.

\n
", + "smithy.api#documentation": "

Returns a list of conformance pack compliance scores. \n\t\t\tA compliance score is the percentage of the number of compliant rule-resource combinations in a conformance pack compared to the number of total possible rule-resource combinations in the conformance pack.\n\t\t\tThis metric provides you with a high-level view of the compliance state of your conformance packs. You can use it to identify, investigate, and understand\n\t\t\tthe level of compliance in your conformance packs.

\n \n

Conformance packs with no evaluation results will have a compliance score of INSUFFICIENT_DATA.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -7293,13 +7585,13 @@ "SortOrder": { "target": "com.amazonaws.configservice#SortOrder", "traits": { - "smithy.api#documentation": "

Determines the order in which conformance pack compliance scores are sorted. Either in ascending or descending order.

\n\t\t\n\t\t

By default, conformance pack compliance scores are sorted in alphabetical order by name of the conformance pack. Conformance pack compliance scores are sorted in reverse alphabetical order if you enter DESCENDING.

\n\t\t\n\t\t

You can sort conformance pack compliance scores by the numerical value of the compliance score by entering SCORE in the SortBy action. When compliance scores are sorted by SCORE, conformance packs with a compliance score of INSUFFICIENT_DATA will be last when sorting by ascending order and first when sorting by descending order.

" + "smithy.api#documentation": "

Determines the order in which conformance pack compliance scores are sorted. Either in ascending or descending order.

\n

By default, conformance pack compliance scores are sorted in alphabetical order by name of the conformance pack. Conformance pack compliance scores are sorted in reverse alphabetical order if you enter DESCENDING.

\n

You can sort conformance pack compliance scores by the numerical value of the compliance score by entering SCORE in the SortBy action. When compliance scores are sorted by SCORE, conformance packs with a compliance score of INSUFFICIENT_DATA will be last when sorting by ascending order and first when sorting by descending order.

" } }, "SortBy": { "target": "com.amazonaws.configservice#SortBy", "traits": { - "smithy.api#documentation": "

Sorts your conformance pack compliance scores in either ascending or descending order, depending on SortOrder.

\n\t\t

By default, conformance pack compliance scores are sorted in alphabetical order by name of the conformance pack.\n\t\t\tEnter SCORE, to sort conformance pack compliance scores by the numerical value of the compliance score.

" + "smithy.api#documentation": "

Sorts your conformance pack compliance scores in either ascending or descending order, depending on SortOrder.

\n

By default, conformance pack compliance scores are sorted in alphabetical order by name of the conformance pack.\n\t\t\tEnter SCORE, to sort conformance pack compliance scores by the numerical value of the compliance score.

" } }, "Limit": { @@ -7312,9 +7604,12 @@ "NextToken": { "target": "com.amazonaws.configservice#NextToken", "traits": { - "smithy.api#documentation": "

The nextToken string in a prior request that you can use to get the paginated response for next set of conformance pack compliance scores.

" + "smithy.api#documentation": "

The nextToken string in a prior request that you can use to get the paginated response for the next set of conformance pack compliance scores.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListConformancePackComplianceScoresResponse": { @@ -7333,6 +7628,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListDiscoveredResources": { @@ -7358,7 +7656,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a resource type and returns a list of resource\n\t\t\tidentifiers for the resources of that type. A resource identifier\n\t\t\tincludes the resource type, ID, and (if available) the custom\n\t\t\tresource name. The results consist of resources that Config has\n\t\t\tdiscovered, including those that Config is not currently\n\t\t\trecording. You can narrow the results to include only resources that\n\t\t\thave specific resource IDs or a resource name.

\n\t\t \n\t\t\t

You can specify either resource IDs or a resource name, but\n\t\t\t\tnot both, in the same request.

\n\t\t
\n\t\t

The response is paginated. By default, Config lists 100\n\t\t\tresource identifiers on each page. You can customize this number\n\t\t\twith the limit parameter. The response includes a\n\t\t\t\tnextToken string. To get the next page of results,\n\t\t\trun the request again and specify the string for the\n\t\t\t\tnextToken parameter.

", + "smithy.api#documentation": "

Accepts a resource type and returns a list of resource\n\t\t\tidentifiers for the resources of that type. A resource identifier\n\t\t\tincludes the resource type, ID, and (if available) the custom\n\t\t\tresource name. The results consist of resources that Config has\n\t\t\tdiscovered, including those that Config is not currently\n\t\t\trecording. You can narrow the results to include only resources that\n\t\t\thave specific resource IDs or a resource name.

\n \n

You can specify either resource IDs or a resource name, but\n\t\t\t\tnot both, in the same request.

\n
\n

The response is paginated. By default, Config lists 100\n\t\t\tresource identifiers on each page. You can customize this number\n\t\t\twith the limit parameter. The response includes a\n\t\t\t\tnextToken string. To get the next page of results,\n\t\t\trun the request again and specify the string for the\n\t\t\t\tnextToken parameter.

", "smithy.api#paginated": { "inputToken": "nextToken", "outputToken": "nextToken", @@ -7380,7 +7678,7 @@ "resourceIds": { "target": "com.amazonaws.configservice#ResourceIdList", "traits": { - "smithy.api#documentation": "

The IDs of only those resources that you want Config to\n\t\t\tlist in the response. If you do not specify this parameter, Config lists all resources of the specified type that it has\n\t\t\tdiscovered.

" + "smithy.api#documentation": "

The IDs of only those resources that you want Config to\n\t\t\tlist in the response. If you do not specify this parameter, Config lists all resources of the specified type that it has\n\t\t\tdiscovered. You can list a minimum of 1 resourceID and a maximum of 20 resourceIds.

" } }, "resourceName": { @@ -7411,7 +7709,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListDiscoveredResourcesResponse": { @@ -7431,7 +7730,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListResourceEvaluations": { @@ -7495,6 +7795,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListResourceEvaluationsResponse": { @@ -7512,6 +7815,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListStoredQueries": { @@ -7555,6 +7861,9 @@ "smithy.api#documentation": "

The maximum number of results to be returned with a single call.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListStoredQueriesResponse": { @@ -7572,6 +7881,9 @@ "smithy.api#documentation": "

If the previous paginated request didn't return all of the remaining results, the response object's NextToken parameter value is set to a token. \n\t\t\tTo retrieve the next set of results, call this action again and assign that token to the request object's NextToken parameter. \n\t\t\tIf there are no remaining results, the previous response object's NextToken parameter is set to null.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListTagsForResource": { @@ -7629,6 +7941,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListTagsForResourceResponse": { @@ -7646,6 +7961,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#Long": { @@ -7695,7 +8013,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of recorders you can\n\t\t\tcreate.

", + "smithy.api#documentation": "

You have reached the limit of the number of configuration recorders you can\n\t\t\tcreate.

", "smithy.api#error": "client" } }, @@ -7710,7 +8028,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of conformance packs you can create in an account. For more information, see \n Service Limits\n in the Config Developer Guide.

", + "smithy.api#documentation": "

You have reached the limit of the number of conformance packs you can create in an account. For more information, see \n Service Limits\n in the Config Developer Guide.

", "smithy.api#error": "client" } }, @@ -7740,7 +8058,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of organization Config rules you can create. For more information, see see \n Service Limits\n in the Config Developer Guide.

", + "smithy.api#documentation": "

You have reached the limit of the number of organization Config rules you can create. For more information, see see \n Service Limits\n in the Config Developer Guide.

", "smithy.api#error": "client" } }, @@ -7755,7 +8073,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of organization conformance packs you can create in an account. For more information, see \n Service Limits\n in the Config Developer Guide.

", + "smithy.api#documentation": "

You have reached the limit of the number of organization conformance packs you can create in an account. For more information, see \n Service Limits\n in the Config Developer Guide.

", "smithy.api#error": "client" } }, @@ -7888,7 +8206,7 @@ "MemberAccountRuleStatus": { "target": "com.amazonaws.configservice#MemberAccountRuleStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status for Config rule in the member account.\n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in the member account. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n\t\t

Config sets the state of the rule to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when Config rule has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when Config rule is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when Config rule creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when Config rule is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when Config rule has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when Config rule has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when Config rule is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status for Config rule in the member account.\n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in the member account. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n

Config sets the state of the rule to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when Config rule has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when Config rule is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when Config rule creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when Config rule is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when Config rule has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when Config rule has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when Config rule is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8036,7 +8354,7 @@ } }, "traits": { - "smithy.api#documentation": "

The Config rule in the request is invalid. Verify that the rule is an Config Custom Policy rule, that the rule name is correct, and that valid Amazon Resouce Names (ARNs) are used before trying again.

", + "smithy.api#documentation": "

The Config rule in the request is not valid. Verify that the rule is an Config Process Check rule, that the rule name is correct, and that valid Amazon Resouce Names (ARNs) are used before trying again.

", "smithy.api#error": "client" } }, @@ -8126,7 +8444,7 @@ } }, "traits": { - "smithy.api#documentation": "

The Config rule in the request is invalid. Verify that the rule is an organization Config Custom Policy rule, that the rule name is correct, and that valid Amazon Resouce Names (ARNs) are used before trying again.

", + "smithy.api#documentation": "

The Config rule in the request is not valid. Verify that the rule is an organization Config Process Check rule, that the rule name is correct, and that valid Amazon Resouce Names (ARNs) are used before trying again.

", "smithy.api#error": "client" } }, @@ -8141,7 +8459,7 @@ } }, "traits": { - "smithy.api#documentation": "

Config organization conformance pack that you passed in the filter does not exist.

\n\t\t

For DeleteOrganizationConformancePack, you tried to delete an organization conformance pack that does not exist.

", + "smithy.api#documentation": "

Config organization conformance pack that you passed in the filter does not exist.

\n

For DeleteOrganizationConformancePack, you tried to delete an organization conformance pack that does not exist.

", "smithy.api#error": "client" } }, @@ -8204,7 +8522,7 @@ } }, "traits": { - "smithy.api#documentation": "

For PutConfigurationAggregator API, you can see this exception for the following reasons:

\n\t\t
    \n
  • \n

    No permission to call EnableAWSServiceAccess API

    \n
  • \n
  • \n

    The configuration aggregator cannot be updated because your Amazon Web Services Organization management account or the delegated administrator role changed. \n\t\t\t\tDelete this aggregator and create a new one with the current Amazon Web Services Organization.

    \n
  • \n
  • \n

    The configuration aggregator is associated with a previous Amazon Web Services Organization and Config cannot aggregate data with current Amazon Web Services Organization. \n\t\t\t\tDelete this aggregator and create a new one with the current Amazon Web Services Organization.

    \n
  • \n
  • \n

    You are not a registered delegated administrator for Config with permissions to call ListDelegatedAdministrators API. \n\t\t\tEnsure that the management account registers delagated administrator for Config service principle name before the delegated administrator creates an aggregator.

    \n
  • \n
\t\n\t\t

For all OrganizationConfigRule and OrganizationConformancePack APIs, Config throws an exception if APIs are called from member accounts. All APIs must be called from organization management account.

", + "smithy.api#documentation": "

For PutConfigurationAggregator API, you can see this exception for the following reasons:

\n
    \n
  • \n

    No permission to call EnableAWSServiceAccess API

    \n
  • \n
  • \n

    The configuration aggregator cannot be updated because your Amazon Web Services Organization management account or the delegated administrator role changed. \n\t\t\t\tDelete this aggregator and create a new one with the current Amazon Web Services Organization.

    \n
  • \n
  • \n

    The configuration aggregator is associated with a previous Amazon Web Services Organization and Config cannot aggregate data with current Amazon Web Services Organization. \n\t\t\t\tDelete this aggregator and create a new one with the current Amazon Web Services Organization.

    \n
  • \n
  • \n

    You are not a registered delegated administrator for Config with permissions to call ListDelegatedAdministrators API. \n\t\t\tEnsure that the management account registers delagated administrator for Config service principle name before the delegated administrator creates an aggregator.

    \n
  • \n
\n

For all OrganizationConfigRule and OrganizationConformancePack APIs, Config throws an exception if APIs are called from member accounts. All APIs must be called from organization management account.

", "smithy.api#error": "client" } }, @@ -8344,7 +8662,7 @@ "OrganizationRuleStatus": { "target": "com.amazonaws.configservice#OrganizationRuleStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status of an organization Config rule. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in all the member accounts. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in all the member accounts. Additionally, Config rule status is updated when one or more member accounts join or leave an organization. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule in all the member accounts and disables service access for config-multiaccountsetup.amazonaws.com.

\n\t\t\t

Config sets the state of the rule to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when an organization Config rule has been successfully created in all the member accounts.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when an organization Config rule creation is in progress.

    \n
  • \n
  • \n

    \n CREATE_FAILED when an organization Config rule creation failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_FAILED when an organization Config rule deletion failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when an organization Config rule deletion is in progress.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when an organization Config rule has been successfully deleted from all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when an organization Config rule has been successfully updated in all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when an organization Config rule update is in progress.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when an organization Config rule update failed in one or more member accounts within that organization.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status of an organization Config rule. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in all the member accounts. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in all the member accounts. Additionally, Config rule status is updated when one or more member accounts join or leave an organization. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule in all the member accounts and disables service access for config-multiaccountsetup.amazonaws.com.

\n

Config sets the state of the rule to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when an organization Config rule has been successfully created in all the member accounts.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when an organization Config rule creation is in progress.

    \n
  • \n
  • \n

    \n CREATE_FAILED when an organization Config rule creation failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_FAILED when an organization Config rule deletion failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when an organization Config rule deletion is in progress.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when an organization Config rule has been successfully deleted from all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when an organization Config rule has been successfully updated in all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when an organization Config rule update is in progress.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when an organization Config rule update failed in one or more member accounts within that organization.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8455,13 +8773,13 @@ "DeliveryS3Bucket": { "target": "com.amazonaws.configservice#DeliveryS3Bucket", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n \n

This field is optional.

\n
" } }, "DeliveryS3KeyPrefix": { "target": "com.amazonaws.configservice#DeliveryS3KeyPrefix", "traits": { - "smithy.api#documentation": "

Any folder structure you want to add to an Amazon S3 bucket.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

Any folder structure you want to add to an Amazon S3 bucket.

\n \n

This field is optional.

\n
" } }, "ConformancePackInputParameters": { @@ -8508,7 +8826,7 @@ "Status": { "target": "com.amazonaws.configservice#OrganizationResourceDetailedStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status for conformance pack in a member account.\n\t\t\tWhen management account calls PutOrganizationConformancePack action for the first time, conformance pack status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConformancePack action for the second time, conformance pack status is updated in the member account. \n\t\t\tConformance pack status is deleted when the management account deletes OrganizationConformancePack and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n\t\t

Config sets the state of the conformance pack to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when conformance pack has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when conformance pack is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when conformance pack creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when conformance pack is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when conformance pack has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when conformance pack has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when conformance pack is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status for conformance pack in a member account.\n\t\t\tWhen management account calls PutOrganizationConformancePack action for the first time, conformance pack status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConformancePack action for the second time, conformance pack status is updated in the member account. \n\t\t\tConformance pack status is deleted when the management account deletes OrganizationConformancePack and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n

Config sets the state of the conformance pack to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when conformance pack has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when conformance pack is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when conformance pack creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when conformance pack is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when conformance pack has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when conformance pack has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when conformance pack is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8576,7 +8894,7 @@ "Status": { "target": "com.amazonaws.configservice#OrganizationResourceStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status of an organization conformance pack. \n\t\t\tWhen management account calls PutOrganizationConformancePack for the first time, \n\t\t\tconformance pack status is created in all the member accounts. \n\t\t\tWhen management account calls PutOrganizationConformancePack for the second time, \n\t\t\tconformance pack status is updated in all the member accounts. \n\t\t\tAdditionally, conformance pack status is updated when one or more member accounts join or leave an \n\t\t\torganization. \n\t\t\tConformance pack status is deleted when the management account deletes \n\t\t\tOrganizationConformancePack in all the member accounts and disables service \n\t\t\taccess for config-multiaccountsetup.amazonaws.com.

\n\t\t

Config sets the state of the conformance pack to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when an organization conformance pack has been successfully created in all the member accounts.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when an organization conformance pack creation is in progress.

    \n
  • \n
  • \n

    \n CREATE_FAILED when an organization conformance pack creation failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_FAILED when an organization conformance pack deletion failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when an organization conformance pack deletion is in progress.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when an organization conformance pack has been successfully deleted from all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when an organization conformance pack has been successfully updated in all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when an organization conformance pack update is in progress.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when an organization conformance pack update failed in one or more member accounts within that organization.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status of an organization conformance pack. \n\t\t\tWhen management account calls PutOrganizationConformancePack for the first time, \n\t\t\tconformance pack status is created in all the member accounts. \n\t\t\tWhen management account calls PutOrganizationConformancePack for the second time, \n\t\t\tconformance pack status is updated in all the member accounts. \n\t\t\tAdditionally, conformance pack status is updated when one or more member accounts join or leave an \n\t\t\torganization. \n\t\t\tConformance pack status is deleted when the management account deletes \n\t\t\tOrganizationConformancePack in all the member accounts and disables service \n\t\t\taccess for config-multiaccountsetup.amazonaws.com.

\n

Config sets the state of the conformance pack to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when an organization conformance pack has been successfully created in all the member accounts.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when an organization conformance pack creation is in progress.

    \n
  • \n
  • \n

    \n CREATE_FAILED when an organization conformance pack creation failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_FAILED when an organization conformance pack deletion failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when an organization conformance pack deletion is in progress.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when an organization conformance pack has been successfully deleted from all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when an organization conformance pack has been successfully updated in all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when an organization conformance pack update is in progress.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when an organization conformance pack update failed in one or more member accounts within that organization.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8620,7 +8938,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have specified a template that is invalid or supported.

", + "smithy.api#documentation": "

You have specified a template that is not valid or supported.

", "smithy.api#error": "client" } }, @@ -8642,7 +8960,7 @@ "OrganizationConfigRuleTriggerTypes": { "target": "com.amazonaws.configservice#OrganizationConfigRuleTriggerTypeNoSNs", "traits": { - "smithy.api#documentation": "

The type of notification that initiates Config to run an evaluation for a rule.\n\t\t\tFor Config Custom Policy rules, Config supports change-initiated notification types:

\n\t\t\n\t\t
    \n
  • \n

    \n ConfigurationItemChangeNotification - Initiates an evaluation when Config delivers a configuration item as a result of a resource\n\t\t\t\t\tchange.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Initiates an evaluation when\n\t\t\t\t\t\tConfig delivers an oversized configuration item. Config may generate this notification type when a resource changes and the\n\t\t\t\t\tnotification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
" + "smithy.api#documentation": "

The type of notification that initiates Config to run an evaluation for a rule.\n\t\t\tFor Config Custom Policy rules, Config supports change-initiated notification types:

\n
    \n
  • \n

    \n ConfigurationItemChangeNotification - Initiates an evaluation when Config delivers a configuration item as a result of a resource\n\t\t\t\t\tchange.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Initiates an evaluation when\n\t\t\t\t\t\tConfig delivers an oversized configuration item. Config may generate this notification type when a resource changes and the\n\t\t\t\t\tnotification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
" } }, "InputParameters": { @@ -8718,7 +9036,7 @@ "OrganizationConfigRuleTriggerTypes": { "target": "com.amazonaws.configservice#OrganizationConfigRuleTriggerTypeNoSNs", "traits": { - "smithy.api#documentation": "

The type of notification that triggers Config to run an evaluation for a rule.\n\t\t\tFor Config Custom Policy rules, Config supports change\n\t\t\ttriggered notification types:

\n\t\t\n\t\t
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers an evaluation when Config delivers a configuration item as a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Triggers an evaluation when Config delivers an oversized configuration item. \n\t\t\t\tConfig may generate this notification type when a resource changes and the notification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
" + "smithy.api#documentation": "

The type of notification that triggers Config to run an evaluation for a rule.\n\t\t\tFor Config Custom Policy rules, Config supports change\n\t\t\ttriggered notification types:

\n
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers an evaluation when Config delivers a configuration item as a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Triggers an evaluation when Config delivers an oversized configuration item. \n\t\t\t\tConfig may generate this notification type when a resource changes and the notification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
" } }, "InputParameters": { @@ -8771,7 +9089,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object that specifies metadata for your organization Config Custom Policy rule including the runtime system in use, which accounts have debug logging enabled, and\n\t\t\tother custom rule metadata such as resource type, resource ID of Amazon Web Services\n\t\t\tresource, and organization trigger types that trigger Config to evaluate\n\t\t\t\tAmazon Web Services resources against a rule.

" + "smithy.api#documentation": "

metadata for your organization Config Custom Policy rule including the runtime system in use, which accounts have debug logging enabled, and\n\t\t\tother custom rule metadata such as resource type, resource ID of Amazon Web Services\n\t\t\tresource, and organization trigger types that trigger Config to evaluate\n\t\t\t\tAmazon Web Services resources against a rule.

" } }, "com.amazonaws.configservice#OrganizationCustomRuleMetadata": { @@ -8793,7 +9111,7 @@ "OrganizationConfigRuleTriggerTypes": { "target": "com.amazonaws.configservice#OrganizationConfigRuleTriggerTypes", "traits": { - "smithy.api#documentation": "

The type of notification that triggers Config to run an evaluation for a rule. You can specify the following notification types:

\n\t\t\n\t\t
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers an evaluation when Config delivers a configuration item as a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Triggers an evaluation when Config delivers an oversized configuration item. \n\t\t\t \tConfig may generate this notification type when a resource changes and the notification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
  • \n

    \n ScheduledNotification - Triggers a periodic evaluation at the frequency specified for MaximumExecutionFrequency.

    \n
  • \n
", + "smithy.api#documentation": "

The type of notification that triggers Config to run an evaluation for a rule. You can specify the following notification types:

\n
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers an evaluation when Config delivers a configuration item as a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Triggers an evaluation when Config delivers an oversized configuration item. \n\t\t\t \tConfig may generate this notification type when a resource changes and the notification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
  • \n

    \n ScheduledNotification - Triggers a periodic evaluation at the frequency specified for MaximumExecutionFrequency.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8806,7 +9124,7 @@ "MaximumExecutionFrequency": { "target": "com.amazonaws.configservice#MaximumExecutionFrequency", "traits": { - "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations for a rule. \n\t\t\tYour custom rule is triggered when Config delivers the configuration snapshot. For more information, see ConfigSnapshotDeliveryProperties.

\n\t\t \n

By default, rules with a periodic trigger are evaluated every 24 hours. To change the frequency, specify a valid \n\t\t\tvalue for the MaximumExecutionFrequency parameter.

\n
" + "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations for a rule. \n\t\t\tYour custom rule is triggered when Config delivers the configuration snapshot. For more information, see ConfigSnapshotDeliveryProperties.

\n \n

By default, rules with a periodic trigger are evaluated every 24 hours. To change the frequency, specify a valid \n\t\t\tvalue for the MaximumExecutionFrequency parameter.

\n
" } }, "ResourceTypesScope": { @@ -8835,7 +9153,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object that specifies organization custom rule metadata such as resource type, resource ID of Amazon Web Services resource, Lambda function ARN, \n\t\t\tand organization trigger types that trigger Config to evaluate your Amazon Web Services resources against a rule. \n\t\t\tIt also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

" + "smithy.api#documentation": "

organization custom rule metadata such as resource type, resource ID of Amazon Web Services resource, Lambda function ARN, \n\t\t\tand organization trigger types that trigger Config to evaluate your Amazon Web Services resources against a rule. \n\t\t\tIt also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

" } }, "com.amazonaws.configservice#OrganizationManagedRuleMetadata": { @@ -8863,7 +9181,7 @@ "MaximumExecutionFrequency": { "target": "com.amazonaws.configservice#MaximumExecutionFrequency", "traits": { - "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations for a rule. This is for an Config managed rule that is triggered at a periodic frequency.

\n\t\t \n

By default, rules with a periodic trigger are evaluated every 24 hours. To change the frequency, specify a valid \n\t\t\tvalue for the MaximumExecutionFrequency parameter.

\n
" + "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations for a rule. This is for an Config managed rule that is triggered at a periodic frequency.

\n \n

By default, rules with a periodic trigger are evaluated every 24 hours. To change the frequency, specify a valid \n\t\t\tvalue for the MaximumExecutionFrequency parameter.

\n
" } }, "ResourceTypesScope": { @@ -8892,7 +9210,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object that specifies organization managed rule metadata such as resource type and ID of Amazon Web Services resource along with the rule identifier. \n\t\t\tIt also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

" + "smithy.api#documentation": "

organization managed rule metadata such as resource type and ID of Amazon Web Services resource along with the rule identifier. \n\t\t\tIt also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

" } }, "com.amazonaws.configservice#OrganizationResourceDetailedStatus": { @@ -8966,7 +9284,7 @@ "Status": { "target": "com.amazonaws.configservice#OrganizationResourceDetailedStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status for conformance pack in a member account.\n\t\t\tWhen management account calls PutOrganizationConformancePack action for the first time, conformance pack status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConformancePack action for the second time, conformance pack status is updated in the member account. \n\t\t\tConformance pack status is deleted when the management account deletes OrganizationConformancePack and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n\t\t

Config sets the state of the conformance pack to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when conformance pack has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when conformance pack is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when conformance pack creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when conformance pack is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when conformance pack has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when conformance pack has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when conformance pack is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
" + "smithy.api#documentation": "

Indicates deployment status for conformance pack in a member account.\n\t\t\tWhen management account calls PutOrganizationConformancePack action for the first time, conformance pack status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConformancePack action for the second time, conformance pack status is updated in the member account. \n\t\t\tConformance pack status is deleted when the management account deletes OrganizationConformancePack and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n

Config sets the state of the conformance pack to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when conformance pack has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when conformance pack is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when conformance pack creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when conformance pack is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when conformance pack has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when conformance pack has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when conformance pack is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
" } } }, @@ -9226,7 +9544,7 @@ } ], "traits": { - "smithy.api#documentation": "

Authorizes the aggregator account and region to collect data\n\t\t\tfrom the source account and region.

" + "smithy.api#documentation": "

Authorizes the aggregator account and region to collect data\n\t\t\tfrom the source account and region.

\n \n

\n PutAggregationAuthorization is an idempotent API. Subsequent requests won’t create a duplicate resource if one was already created. If a following request has different tags values,\n\t\t\tConfig will ignore these differences and treat it as an idempotent request of the previous. In this case, tags will not be updated, even if they are different.

\n
" } }, "com.amazonaws.configservice#PutAggregationAuthorizationRequest": { @@ -9252,6 +9570,9 @@ "smithy.api#documentation": "

An array of tag object.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutAggregationAuthorizationResponse": { @@ -9263,6 +9584,9 @@ "smithy.api#documentation": "

Returns an AggregationAuthorization object.\n\t\t\t\n\t\t

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutConfigRule": { @@ -9291,7 +9615,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates an Config rule to evaluate if your\n\t\t\tAmazon Web Services resources comply with your desired configurations. For information on how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n\t\t\n\t\t

There are two types of rules: Config Custom Rules and Config Managed Rules.\n\t\t\tYou can use PutConfigRule to create both Config custom rules and Config managed rules.

\n\t\t\n\t\t

Custom rules are rules that you can create using either Guard or Lambda functions.\n\t\t\tGuard (Guard GitHub\n\t\t\t\tRepository) is a policy-as-code language that allows you to write policies that\n\t\t\tare enforced by Config Custom Policy rules. Lambda uses custom code that you upload to\n\t\t\tevaluate a custom rule. If you are adding a new Custom Lambda rule,\n\t\t\tyou first need to create an Lambda function that the rule invokes to evaluate\n\t\t\tyour resources. When you use PutConfigRule to add a Custom Lambda rule to Config, you must specify the Amazon Resource\n\t\t\tName (ARN) that Lambda assigns to the function. You specify the ARN\n\t\t\tin the SourceIdentifier key. This key is part of the\n\t\t\tSource object, which is part of the\n\t\t\tConfigRule object.

\n\t\t\n\t\t

Managed rules are predefined,\n\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\tList of Config\n\t\t\t\tManaged Rules. If you are adding an Config managed rule, you must specify the\n\t\t\trule's identifier for the SourceIdentifier key.

\n\t\t\n\t\t

For any new rule that you add, specify the\n\t\t\t\tConfigRuleName in the ConfigRule\n\t\t\tobject. Do not specify the ConfigRuleArn or the\n\t\t\tConfigRuleId. These values are generated by Config for new rules.

\n\t\t

If you are updating a rule that you added previously, you can\n\t\t\tspecify the rule by ConfigRuleName,\n\t\t\t\tConfigRuleId, or ConfigRuleArn in the\n\t\t\t\tConfigRule data type that you use in this\n\t\t\trequest.

\n\n\t\t

For more information about developing and using Config\n\t\t\trules, see Evaluating Amazon Web Services resource Configurations with Config\n\t\t\tin the Config Developer Guide.

" + "smithy.api#documentation": "

Adds or updates an Config rule to evaluate if your\n\t\t\tAmazon Web Services resources comply with your desired configurations. For information on how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

There are two types of rules: Config Managed Rules and Config Custom Rules.\n\t\t\tYou can use PutConfigRule to create both Config Managed Rules and Config Custom Rules.

\n

Config Managed Rules are predefined,\n\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\tList of Config\n\t\t\t\tManaged Rules. If you are adding an Config managed rule, you must specify the\n\t\t\trule's identifier for the SourceIdentifier key.

\n

Config Custom Rules are rules that you create from scratch. There are two ways to create Config custom rules: with Lambda functions\n\t\t\t( Lambda Developer Guide) and with Guard (Guard GitHub\n\t\t\t\t\tRepository), a policy-as-code language.\n\t\t\t\n\t\t\tConfig custom rules created with Lambda\n\t\t\tare called Config Custom Lambda Rules and Config custom rules created with\n\t\t\tGuard are called Config Custom Policy Rules.

\n

If you are adding a new Config Custom Lambda rule,\n\t\t\tyou first need to create an Lambda function that the rule invokes to evaluate\n\t\t\tyour resources. When you use PutConfigRule to add a Custom Lambda rule to Config, you must specify the Amazon Resource\n\t\t\tName (ARN) that Lambda assigns to the function. You specify the ARN\n\t\t\tin the SourceIdentifier key. This key is part of the\n\t\t\tSource object, which is part of the\n\t\t\tConfigRule object.

\n

For any new Config rule that you add, specify the\n\t\t\t\tConfigRuleName in the ConfigRule\n\t\t\tobject. Do not specify the ConfigRuleArn or the\n\t\t\tConfigRuleId. These values are generated by Config for new rules.

\n

If you are updating a rule that you added previously, you can\n\t\t\tspecify the rule by ConfigRuleName,\n\t\t\t\tConfigRuleId, or ConfigRuleArn in the\n\t\t\t\tConfigRule data type that you use in this\n\t\t\trequest.

\n

For more information about developing and using Config\n\t\t\trules, see Evaluating Resources with Config Rules\n\t\t\tin the Config Developer Guide.

\n \n

\n PutConfigRule is an idempotent API. Subsequent requests won’t create a duplicate resource if one was already created. If a following request has different tags values,\n\t\t\tConfig will ignore these differences and treat it as an idempotent request of the previous. In this case, tags will not be updated, even if they are different.

\n
" } }, "com.amazonaws.configservice#PutConfigRuleRequest": { @@ -9310,6 +9634,9 @@ "smithy.api#documentation": "

An array of tag object.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutConfigurationAggregator": { @@ -9341,7 +9668,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates and updates the configuration aggregator with the\n\t\t\tselected source accounts and regions. The source account can be\n\t\t\tindividual account(s) or an organization.

\n\t\t\n\t\t

\n accountIds that are passed will be replaced with existing accounts.\n\t\t\tIf you want to add additional accounts into the aggregator, call DescribeConfigurationAggregators to get the previous accounts and then append new ones.

\n\t\t \n\t\t\t

Config should be enabled in source accounts and regions\n\t\t\t\tyou want to aggregate.

\n\t\t\t\n\t\t\t

If your source type is an organization, you must be signed in to the management account or a registered delegated administrator and all the features must be enabled in your organization. \n\t\t\t\tIf the caller is a management account, Config calls EnableAwsServiceAccess API to enable integration between Config and Organizations.\n\t\t\t\tIf the caller is a registered delegated administrator, Config calls ListDelegatedAdministrators API to verify whether the caller is a valid delegated administrator.

\n\t\t\t

To register a delegated administrator, see Register a Delegated Administrator in the Config developer guide.

\n\t\t
" + "smithy.api#documentation": "

Creates and updates the configuration aggregator with the\n\t\t\tselected source accounts and regions. The source account can be\n\t\t\tindividual account(s) or an organization.

\n

\n accountIds that are passed will be replaced with existing accounts.\n\t\t\tIf you want to add additional accounts into the aggregator, call DescribeConfigurationAggregators to get the previous accounts and then append new ones.

\n \n

Config should be enabled in source accounts and regions\n\t\t\t\tyou want to aggregate.

\n

If your source type is an organization, you must be signed in to the management account or a registered delegated administrator and all the features must be enabled in your organization. \n\t\t\t\tIf the caller is a management account, Config calls EnableAwsServiceAccess API to enable integration between Config and Organizations.\n\t\t\t\tIf the caller is a registered delegated administrator, Config calls ListDelegatedAdministrators API to verify whether the caller is a valid delegated administrator.

\n

To register a delegated administrator, see Register a Delegated Administrator in the Config developer guide.

\n
\n \n

\n PutConfigurationAggregator is an idempotent API. Subsequent requests won’t create a duplicate resource if one was already created. If a following request has different tags values,\n\t\t\tConfig will ignore these differences and treat it as an idempotent request of the previous. In this case, tags will not be updated, even if they are different.

\n
" } }, "com.amazonaws.configservice#PutConfigurationAggregatorRequest": { @@ -9372,6 +9699,9 @@ "smithy.api#documentation": "

An array of tag object.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutConfigurationAggregatorResponse": { @@ -9383,6 +9713,9 @@ "smithy.api#documentation": "

Returns a ConfigurationAggregator object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutConfigurationRecorder": { @@ -9408,7 +9741,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new configuration recorder to record the selected\n\t\t\tresource configurations.

\n\t\t

You can use this action to change the role roleARN\n\t\t\tor the recordingGroup of an existing recorder. To\n\t\t\tchange the role, call the action on the existing configuration\n\t\t\trecorder and specify a role.

\n\t\t \n\t\t\t

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n\t\t\t

If ConfigurationRecorder does not have the\n\t\t\t\t\trecordingGroup parameter\n\t\t\t\tspecified, the default is to record all supported resource\n\t\t\t\ttypes.

\n\t\t
" + "smithy.api#documentation": "

Creates a new configuration recorder to record configuration changes for specified resource types.

\n

You can also use this action to change the roleARN\n\t\t\tor the recordingGroup of an existing recorder.\n\t\t\tFor more information, see \n Managing the Configuration Recorder\n in the Config Developer Guide.

\n \n

You can specify only one configuration recorder for each Amazon Web Services Region for each account.

\n

If the configuration recorder does not have the\n\t\t\t\t\trecordingGroup field\n\t\t\t\tspecified, the default is to record all supported resource\n\t\t\t\ttypes.

\n
" } }, "com.amazonaws.configservice#PutConfigurationRecorderRequest": { @@ -9417,13 +9750,14 @@ "ConfigurationRecorder": { "target": "com.amazonaws.configservice#ConfigurationRecorder", "traits": { - "smithy.api#documentation": "

The configuration recorder object that records each\n\t\t\tconfiguration change made to the resources.

", + "smithy.api#documentation": "

An object for the configuration recorder to record configuration changes for specified resource types.

", "smithy.api#required": {} } } }, "traits": { - "smithy.api#documentation": "

The input for the PutConfigurationRecorder\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the PutConfigurationRecorder\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutConformancePack": { @@ -9452,7 +9786,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates or updates a conformance pack. A conformance pack is a collection of Config rules that can be easily deployed in an account and a region and across an organization.\n\t\t\tFor information on how many conformance packs you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n\t\t

This API creates a service-linked role AWSServiceRoleForConfigConforms in your account. \n\t\tThe service-linked role is created only when the role does not exist in your account.

\n\t\t \n

You must specify only one of the follow parameters: TemplateS3Uri, TemplateBody or TemplateSSMDocumentDetails.

\n
" + "smithy.api#documentation": "

Creates or updates a conformance pack. A conformance pack is a collection of Config rules that can be easily deployed in an account and a region and across an organization.\n\t\t\tFor information on how many conformance packs you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

This API creates a service-linked role AWSServiceRoleForConfigConforms in your account. \n\t\tThe service-linked role is created only when the role does not exist in your account.

\n \n

You must specify only one of the follow parameters: TemplateS3Uri, TemplateBody or TemplateSSMDocumentDetails.

\n
" } }, "com.amazonaws.configservice#PutConformancePackRequest": { @@ -9468,25 +9802,25 @@ "TemplateS3Uri": { "target": "com.amazonaws.configservice#TemplateS3Uri", "traits": { - "smithy.api#documentation": "

The location of the file containing the template body (s3://bucketname/prefix). The uri must point to a conformance pack template (max size: 300 KB) that is located in an Amazon S3 bucket in the same Region as the conformance pack.

\n\t\t \n

You must have access to read Amazon S3 bucket.

\n
" + "smithy.api#documentation": "

The location of the file containing the template body (s3://bucketname/prefix). The uri must point to a conformance pack template (max size: 300 KB) that is located in an Amazon S3 bucket in the same Region as the conformance pack.

\n \n

You must have access to read Amazon S3 bucket.

\n
" } }, "TemplateBody": { "target": "com.amazonaws.configservice#TemplateBody", "traits": { - "smithy.api#documentation": "

A string containing the full conformance pack template body. The structure containing the template body has a minimum length of 1 byte and a maximum length of 51,200 bytes.

\n\t\t \n

You can use a YAML template with two resource types: Config rule (AWS::Config::ConfigRule) and remediation action (AWS::Config::RemediationConfiguration).

\n
" + "smithy.api#documentation": "

A string containing the full conformance pack template body. The structure containing the template body has a minimum length of 1 byte and a maximum length of 51,200 bytes.

\n \n

You can use a YAML template with two resource types: Config rule (AWS::Config::ConfigRule) and remediation action (AWS::Config::RemediationConfiguration).

\n
" } }, "DeliveryS3Bucket": { "target": "com.amazonaws.configservice#DeliveryS3Bucket", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n \n

This field is optional.

\n
" } }, "DeliveryS3KeyPrefix": { "target": "com.amazonaws.configservice#DeliveryS3KeyPrefix", "traits": { - "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n \n

This field is optional.

\n
" } }, "ConformancePackInputParameters": { @@ -9501,6 +9835,9 @@ "smithy.api#documentation": "

An object of type TemplateSSMDocumentDetails, which contains the name or the Amazon Resource Name (ARN) of the Amazon Web Services Systems Manager document (SSM document) and the version of the SSM document that is used to create a conformance pack.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutConformancePackResponse": { @@ -9512,6 +9849,9 @@ "smithy.api#documentation": "

ARN of the conformance pack.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutDeliveryChannel": { @@ -9549,7 +9889,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a delivery channel object to deliver configuration\n\t\t\tinformation to an Amazon S3 bucket and Amazon SNS topic.

\n\t\t

Before you can create a delivery channel, you must create a\n\t\t\tconfiguration recorder.

\n\t\t

You can use this action to change the Amazon S3 bucket or an\n\t\t\tAmazon SNS topic of the existing delivery channel. To change the\n\t\t\tAmazon S3 bucket or an Amazon SNS topic, call this action and\n\t\t\tspecify the changed values for the S3 bucket and the SNS topic. If\n\t\t\tyou specify a different value for either the S3 bucket or the SNS\n\t\t\ttopic, this action will keep the existing value for the parameter\n\t\t\tthat is not changed.

\n\t\t \n\t\t\t

You can have only one delivery channel per region in your\n\t\t\t\taccount.

\n\t\t\t\n\n\t\t
" + "smithy.api#documentation": "

Creates a delivery channel object to deliver configuration\n\t\t\tinformation to an Amazon S3 bucket and Amazon SNS topic.

\n

Before you can create a delivery channel, you must create a\n\t\t\tconfiguration recorder.

\n

You can use this action to change the Amazon S3 bucket or an\n\t\t\tAmazon SNS topic of the existing delivery channel. To change the\n\t\t\tAmazon S3 bucket or an Amazon SNS topic, call this action and\n\t\t\tspecify the changed values for the S3 bucket and the SNS topic. If\n\t\t\tyou specify a different value for either the S3 bucket or the SNS\n\t\t\ttopic, this action will keep the existing value for the parameter\n\t\t\tthat is not changed.

\n \n

You can have only one delivery channel per region in your\n\t\t\t\taccount.

\n
" } }, "com.amazonaws.configservice#PutDeliveryChannelRequest": { @@ -9564,7 +9904,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the PutDeliveryChannel\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the PutDeliveryChannel\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutEvaluations": { @@ -9610,12 +9951,13 @@ "target": "com.amazonaws.configservice#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Use this parameter to specify a test run for\n\t\t\tPutEvaluations. You can verify whether your Lambda function will deliver evaluation results to Config. No\n\t\t\tupdates occur to your existing evaluations, and evaluation results\n\t\t\tare not sent to Config.

\n\n\t\t \n\t\t\t

When TestMode is true,\n\t\t\t\t\tPutEvaluations doesn't require a valid value\n\t\t\t\tfor the ResultToken parameter, but the value cannot\n\t\t\t\tbe null.

\n\t\t
" + "smithy.api#documentation": "

Use this parameter to specify a test run for\n\t\t\tPutEvaluations. You can verify whether your Lambda function will deliver evaluation results to Config. No\n\t\t\tupdates occur to your existing evaluations, and evaluation results\n\t\t\tare not sent to Config.

\n \n

When TestMode is true,\n\t\t\t\t\tPutEvaluations doesn't require a valid value\n\t\t\t\tfor the ResultToken parameter, but the value cannot\n\t\t\t\tbe null.

\n
" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutEvaluationsResponse": { @@ -9629,7 +9971,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutExternalEvaluation": { @@ -9669,11 +10012,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutExternalEvaluationResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.configservice#PutOrganizationConfigRule": { "type": "operation", @@ -9710,7 +10059,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates an Config rule for your entire organization to evaluate if your Amazon Web Services resources comply with your \n\t\t\tdesired configurations. For information on how many organization Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n\t

Only a management account and a delegated administrator can create or update an organization Config rule.\n\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n\t\t

This API enables organization service access through the EnableAWSServiceAccess action and creates a service-linked \n\t\t\trole AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tConfig verifies the existence of role with GetRole action.

\n\t\t

To use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization\n\t\t\tregister-delegated-administrator for config-multiaccountsetup.amazonaws.com.

\n\t\n\t\t

There are two types of rules: Config Custom Rules and Config Managed Rules.\n\t\t\tYou can use PutOrganizationConfigRule to create both Config custom rules and Config managed rules.

\n\t\t\t\n\t\t

Custom rules are rules that you can create using either Guard or Lambda functions.\n\t\t\tGuard (Guard GitHub\n\t\t\t\tRepository) is a policy-as-code language that allows you to write policies that\n\t\t\tare enforced by Config Custom Policy rules. Lambda uses custom code that you upload to\n\t\t\tevaluate a custom rule. If you are adding a new Custom Lambda rule, you first need to create an Lambda function in the management account or a delegated \n\t\tadministrator that the rule invokes to evaluate your resources. You also need to create an IAM role in the managed account that can be assumed by the Lambda function.\n\t\tWhen you use PutOrganizationConfigRule to add a Custom Lambda rule to Config, you must \n\t\t\tspecify the Amazon Resource Name (ARN) that Lambda assigns to the function.

\n\t\t\n\t\t

Managed rules are predefined,\n\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\tList of Config\n\t\t\t\tManaged Rules. If you are adding an Config managed rule, you must specify the rule's identifier for the RuleIdentifier key.

\n\t\t\n\t\t\n\t\t \n

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n\t\t\t

Make sure to specify one of either OrganizationCustomPolicyRuleMetadata for Custom Policy rules, OrganizationCustomRuleMetadata for Custom Lambda rules, or OrganizationManagedRuleMetadata for managed rules.

\n\t\t\t
" + "smithy.api#documentation": "

Adds or updates an Config rule for your entire organization to evaluate if your Amazon Web Services resources comply with your \n\t\t\tdesired configurations. For information on how many organization Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

Only a management account and a delegated administrator can create or update an organization Config rule.\n\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n

This API enables organization service access through the EnableAWSServiceAccess action and creates a service-linked \n\t\t\trole AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tConfig verifies the existence of role with GetRole action.

\n

To use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization\n\t\t\tregister-delegated-administrator for config-multiaccountsetup.amazonaws.com.

\n

There are two types of rules: Config Managed Rules and Config Custom Rules. \n\t\t\tYou can use PutOrganizationConfigRule to create both Config Managed Rules and Config Custom Rules.

\n

Config Managed Rules are predefined,\n\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\tList of Config\n\t\t\t\tManaged Rules. If you are adding an Config managed rule, you must specify the rule's identifier for the RuleIdentifier key.

\n

Config Custom Rules are rules that you create from scratch. There are two ways to create Config custom rules: with Lambda functions\n\t\t\t( Lambda Developer Guide) and with Guard (Guard GitHub\n\t\t\t\t\tRepository), a policy-as-code language.\n\t\t\t\n\t\t\tConfig custom rules created with Lambda\n\t\t\tare called Config Custom Lambda Rules and Config custom rules created with\n\t\t\tGuard are called Config Custom Policy Rules.

\n

If you are adding a new Config Custom Lambda rule, you first need to create an Lambda function in the management account or a delegated \n\t\tadministrator that the rule invokes to evaluate your resources. You also need to create an IAM role in the managed account that can be assumed by the Lambda function.\n\t\tWhen you use PutOrganizationConfigRule to add a Custom Lambda rule to Config, you must \n\t\t\tspecify the Amazon Resource Name (ARN) that Lambda assigns to the function.

\n \n

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n

Make sure to specify one of either OrganizationCustomPolicyRuleMetadata for Custom Policy rules, OrganizationCustomRuleMetadata for Custom Lambda rules, or OrganizationManagedRuleMetadata for managed rules.

\n
" } }, "com.amazonaws.configservice#PutOrganizationConfigRuleRequest": { @@ -9747,6 +10096,9 @@ "smithy.api#documentation": "

An OrganizationCustomPolicyRuleMetadata object. This object specifies metadata for your organization's Config Custom Policy rule. The metadata includes the runtime system in use, which accounts have debug\n\t\t\tlogging enabled, and other custom rule metadata, such as resource type, resource ID of\n\t\t\tAmazon Web Services resource, and organization trigger types that initiate Config to evaluate Amazon Web Services resources against a rule.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutOrganizationConfigRuleResponse": { @@ -9758,6 +10110,9 @@ "smithy.api#documentation": "

The Amazon Resource Name (ARN) of an organization Config rule.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutOrganizationConformancePack": { @@ -9795,7 +10150,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deploys conformance packs across member accounts in an Amazon Web Services Organization. For information on how many organization conformance packs and how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n\t\t

Only a management account and a delegated administrator can call this API. \n\t\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n\t\t

This API enables organization service access for config-multiaccountsetup.amazonaws.com\n\t\t\tthrough the EnableAWSServiceAccess action and creates a \n\t\t\tservice-linked role AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tTo use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization \n\t\t\tregister-delegate-admin for config-multiaccountsetup.amazonaws.com.

\n\n\t\t\t\n\t\t\t \n\t\t \n\t\t\t

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n\t\t\t

You must specify either the TemplateS3Uri or the TemplateBody parameter, but not both. \n\t\t\tIf you provide both Config uses the TemplateS3Uri parameter and ignores the TemplateBody parameter.

\n\t\t\t

Config sets the state of a conformance pack to CREATE_IN_PROGRESS and UPDATE_IN_PROGRESS until the conformance pack is created or updated. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

\n
" + "smithy.api#documentation": "

Deploys conformance packs across member accounts in an Amazon Web Services Organization. For information on how many organization conformance packs and how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

Only a management account and a delegated administrator can call this API. \n\t\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n

This API enables organization service access for config-multiaccountsetup.amazonaws.com\n\t\t\tthrough the EnableAWSServiceAccess action and creates a \n\t\t\tservice-linked role AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tTo use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization \n\t\t\tregister-delegate-admin for config-multiaccountsetup.amazonaws.com.

\n \n

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n

You must specify either the TemplateS3Uri or the TemplateBody parameter, but not both. \n\t\t\tIf you provide both Config uses the TemplateS3Uri parameter and ignores the TemplateBody parameter.

\n

Config sets the state of a conformance pack to CREATE_IN_PROGRESS and UPDATE_IN_PROGRESS until the conformance pack is created or updated. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

\n
" } }, "com.amazonaws.configservice#PutOrganizationConformancePackRequest": { @@ -9811,7 +10166,7 @@ "TemplateS3Uri": { "target": "com.amazonaws.configservice#TemplateS3Uri", "traits": { - "smithy.api#documentation": "

Location of file containing the template body. The uri must point to the conformance pack template\n\t\t\t(max size: 300 KB).

\n\t\t \n

You must have access to read Amazon S3 bucket.

\n
" + "smithy.api#documentation": "

Location of file containing the template body. The uri must point to the conformance pack template\n\t\t\t(max size: 300 KB).

\n \n

You must have access to read Amazon S3 bucket.

\n
" } }, "TemplateBody": { @@ -9823,13 +10178,13 @@ "DeliveryS3Bucket": { "target": "com.amazonaws.configservice#DeliveryS3Bucket", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n\t\t \n

This field is optional. If used, it must be prefixed with awsconfigconforms.

\n
" + "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n \n

This field is optional. If used, it must be prefixed with awsconfigconforms.

\n
" } }, "DeliveryS3KeyPrefix": { "target": "com.amazonaws.configservice#DeliveryS3KeyPrefix", "traits": { - "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n \n

This field is optional.

\n
" } }, "ConformancePackInputParameters": { @@ -9844,6 +10199,9 @@ "smithy.api#documentation": "

A list of Amazon Web Services accounts to be excluded from an organization conformance pack while deploying a conformance pack.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutOrganizationConformancePackResponse": { @@ -9855,6 +10213,9 @@ "smithy.api#documentation": "

ARN of the organization conformance pack.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutRemediationConfigurations": { @@ -9874,7 +10235,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates the remediation configuration with a specific Config rule with the \n\t\t\tselected target or action. \n\t\t\tThe API creates the RemediationConfiguration object for the Config rule. \n\t\tThe Config rule must already exist for you to add a remediation configuration. \n\t\tThe target (SSM document) must exist and have permissions to use the target.

\n\t\t \n

If you make backward incompatible changes to the SSM document, \n\t\t\tyou must call this again to ensure the remediations can run.

\n\t\t\t

This API does not support adding remediation configurations for service-linked Config Rules such as Organization Config rules, \n\t\t\t\tthe rules deployed by conformance packs, and rules deployed by Amazon Web Services Security Hub.

\n
\n\t\t \n

For manual remediation configuration, you need to provide a value for automationAssumeRole or use a value in the assumeRolefield to remediate your resources. The SSM automation document can use either as long as it maps to a valid parameter.

\n\t\t\t

However, for automatic remediation configuration, the only valid assumeRole field value is AutomationAssumeRole and you need to provide a value for AutomationAssumeRole to remediate your resources.

\n\t\t
" + "smithy.api#documentation": "

Adds or updates the remediation configuration with a specific Config rule with the \n\t\t\tselected target or action. \n\t\t\tThe API creates the RemediationConfiguration object for the Config rule. \n\t\tThe Config rule must already exist for you to add a remediation configuration. \n\t\tThe target (SSM document) must exist and have permissions to use the target.

\n \n

If you make backward incompatible changes to the SSM document, \n\t\t\tyou must call this again to ensure the remediations can run.

\n

This API does not support adding remediation configurations for service-linked Config Rules such as Organization Config rules, \n\t\t\t\tthe rules deployed by conformance packs, and rules deployed by Amazon Web Services Security Hub.

\n
\n \n

For manual remediation configuration, you need to provide a value for automationAssumeRole or use a value in the assumeRolefield to remediate your resources. The SSM automation document can use either as long as it maps to a valid parameter.

\n

However, for automatic remediation configuration, the only valid assumeRole field value is AutomationAssumeRole and you need to provide a value for AutomationAssumeRole to remediate your resources.

\n
" } }, "com.amazonaws.configservice#PutRemediationConfigurationsRequest": { @@ -9887,6 +10248,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutRemediationConfigurationsResponse": { @@ -9898,6 +10262,9 @@ "smithy.api#documentation": "

Returns a list of failed remediation batch objects.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutRemediationExceptions": { @@ -9917,7 +10284,7 @@ } ], "traits": { - "smithy.api#documentation": "

A remediation exception is when a specific resource is no longer considered for auto-remediation. \n\t\t\tThis API adds a new exception or updates an existing exception for a specific resource with a specific Config rule.

\n\t\t \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
\n\t\t \n

To place an exception on an Amazon Web Services resource, ensure remediation is set as manual remediation.

\n
" + "smithy.api#documentation": "

A remediation exception is when a specified resource is no longer considered for auto-remediation. \n\t\t\tThis API adds a new exception or updates an existing exception for a specified resource with a specified Config rule.

\n \n

Config generates a remediation exception when a problem occurs running a remediation action for a specified resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
\n \n

When placing an exception on an Amazon Web Services resource, it is recommended that remediation is set as manual remediation until\n\t\t\tthe given Config rule for the specified resource evaluates the resource as NON_COMPLIANT.\n\t\t\tOnce the resource has been evaluated as NON_COMPLIANT, you can add remediation exceptions and change the remediation type back from Manual to Auto if you want to use auto-remediation.\n\t\t\tOtherwise, using auto-remediation before a NON_COMPLIANT evaluation result can delete resources before the exception is applied.

\n
\n \n

Placing an exception can only be performed on resources that are NON_COMPLIANT.\n\t\t\tIf you use this API for COMPLIANT resources or resources that are NOT_APPLICABLE, a remediation exception will not be generated.\n\t\t\tFor more information on the conditions that initiate the possible Config evaluation results,\n\t\t\tsee Concepts | Config Rules in the Config Developer Guide.

\n
" } }, "com.amazonaws.configservice#PutRemediationExceptionsRequest": { @@ -9949,6 +10316,9 @@ "smithy.api#documentation": "

The exception is automatically deleted after the expiration date.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutRemediationExceptionsResponse": { @@ -9960,6 +10330,9 @@ "smithy.api#documentation": "

Returns a list of failed remediation exceptions batch objects. Each object in the batch consists of a list of failed items and failure messages.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutResourceConfig": { @@ -9985,7 +10358,7 @@ } ], "traits": { - "smithy.api#documentation": "

Records the configuration state for the resource provided in the request.\n\t\t\t \n\t\t\tThe configuration state of a resource is represented in Config as Configuration Items. \n\t\t\tOnce this API records the configuration item, you can retrieve the list of configuration items for the custom resource type using existing Config APIs.

\n\t\t \n

The custom resource type must be registered with CloudFormation. This API accepts the configuration item registered with CloudFormation.

\n\t\t\t

When you call this API, Config only stores configuration state of the resource provided in the request. This API does not change or remediate the configuration of the resource.\n\t\t\t\t

\n\t\t

Write-only schema properites are not recorded as part of the published configuration item.

\n
" + "smithy.api#documentation": "

Records the configuration state for the resource provided in the request.\n\t\t\t \n\t\t\tThe configuration state of a resource is represented in Config as Configuration Items. \n\t\t\tOnce this API records the configuration item, you can retrieve the list of configuration items for the custom resource type using existing Config APIs.

\n \n

The custom resource type must be registered with CloudFormation. This API accepts the configuration item registered with CloudFormation.

\n

When you call this API, Config only stores configuration state of the resource provided in the request. This API does not change or remediate the configuration of the resource.\n\t\t\t\t

\n

Write-only schema properites are not recorded as part of the published configuration item.

\n
" } }, "com.amazonaws.configservice#PutResourceConfigRequest": { @@ -9994,7 +10367,7 @@ "ResourceType": { "target": "com.amazonaws.configservice#ResourceTypeString", "traits": { - "smithy.api#documentation": "

The type of the resource. The custom resource type must be registered with CloudFormation.

\n\t\t \n

You cannot use the organization names “amzn”, “amazon”, “alexa”, “custom” with custom resource types. It is the first part of the ResourceType up to the first ::.

\n
", + "smithy.api#documentation": "

The type of the resource. The custom resource type must be registered with CloudFormation.

\n \n

You cannot use the organization names “amzn”, “amazon”, “alexa”, “custom” with custom resource types. It is the first part of the ResourceType up to the first ::.

\n
", "smithy.api#required": {} } }, @@ -10021,16 +10394,19 @@ "Configuration": { "target": "com.amazonaws.configservice#Configuration", "traits": { - "smithy.api#documentation": "

The configuration object of the resource in valid JSON format. It must match the schema registered with CloudFormation.

\n\t\t \n

The configuration JSON must not exceed 64 KB.

\n
", + "smithy.api#documentation": "

The configuration object of the resource in valid JSON format. It must match the schema registered with CloudFormation.

\n \n

The configuration JSON must not exceed 64 KB.

\n
", "smithy.api#required": {} } }, "Tags": { "target": "com.amazonaws.configservice#Tags", "traits": { - "smithy.api#documentation": "

Tags associated with the resource.

\n\t\t \n

This field is not to be confused with the Amazon Web Services-wide tag feature for Amazon Web Services resources.\n\t\t\tTags for PutResourceConfig are tags that you supply for the configuration items of your custom resources.

\n
" + "smithy.api#documentation": "

Tags associated with the resource.

\n \n

This field is not to be confused with the Amazon Web Services-wide tag feature for Amazon Web Services resources.\n\t\t\tTags for PutResourceConfig are tags that you supply for the configuration items of your custom resources.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutRetentionConfiguration": { @@ -10050,7 +10426,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates and updates the retention configuration with details\n\t\t\tabout retention period (number of days) that Config stores your\n\t\t\thistorical information. The API creates the\n\t\t\t\tRetentionConfiguration object and names the object\n\t\t\tas default. When you have a\n\t\t\t\tRetentionConfiguration object named default, calling the API modifies the\n\t\t\tdefault object.

\n\t\t \n\t\t\t

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n\t\t
" + "smithy.api#documentation": "

Creates and updates the retention configuration with details\n\t\t\tabout retention period (number of days) that Config stores your\n\t\t\thistorical information. The API creates the\n\t\t\t\tRetentionConfiguration object and names the object\n\t\t\tas default. When you have a\n\t\t\t\tRetentionConfiguration object named default, calling the API modifies the\n\t\t\tdefault object.

\n \n

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n
" } }, "com.amazonaws.configservice#PutRetentionConfigurationRequest": { @@ -10060,10 +10436,13 @@ "target": "com.amazonaws.configservice#RetentionPeriodInDays", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Number of days Config stores your historical\n\t\t\tinformation.

\n\t\t \n\t\t\t

Currently, only applicable to the configuration item\n\t\t\t\thistory.

\n\t\t
", + "smithy.api#documentation": "

Number of days Config stores your historical\n\t\t\tinformation.

\n \n

Currently, only applicable to the configuration item\n\t\t\t\thistory.

\n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutRetentionConfigurationResponse": { @@ -10075,6 +10454,9 @@ "smithy.api#documentation": "

Returns a retention configuration object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutStoredQuery": { @@ -10097,7 +10479,7 @@ } ], "traits": { - "smithy.api#documentation": "

Saves a new query or updates an existing saved query. The QueryName must be unique for a single Amazon Web Services account and a single Amazon Web Services Region.\n\t\t\tYou can create upto 300 queries in a single Amazon Web Services account and a single Amazon Web Services Region.

" + "smithy.api#documentation": "

Saves a new query or updates an existing saved query. The QueryName must be unique for a single Amazon Web Services account and a single Amazon Web Services Region.\n\t\t\tYou can create upto 300 queries in a single Amazon Web Services account and a single Amazon Web Services Region.

\n \n

\n PutStoredQuery is an idempotent API. Subsequent requests won’t create a duplicate resource if one was already created. If a following request has different tags values,\n\t\t\tConfig will ignore these differences and treat it as an idempotent request of the previous. In this case, tags will not be updated, even if they are different.

\n
" } }, "com.amazonaws.configservice#PutStoredQueryRequest": { @@ -10106,7 +10488,7 @@ "StoredQuery": { "target": "com.amazonaws.configservice#StoredQuery", "traits": { - "smithy.api#documentation": "

A list of StoredQuery objects. \n\t\t\tThe mandatory fields are QueryName and Expression.

\n\t\t \n

When you are creating a query, you must provide a query name and an expression. \n\t\t\tWhen you are updating a query, you must provide a query name but updating the description is optional.

\n
", + "smithy.api#documentation": "

A list of StoredQuery objects. \n\t\t\tThe mandatory fields are QueryName and Expression.

\n \n

When you are creating a query, you must provide a query name and an expression. \n\t\t\tWhen you are updating a query, you must provide a query name but updating the description is optional.

\n
", "smithy.api#required": {} } }, @@ -10116,6 +10498,9 @@ "smithy.api#documentation": "

A list of Tags object.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutStoredQueryResponse": { @@ -10127,6 +10512,9 @@ "smithy.api#documentation": "

Amazon Resource Name (ARN) of the query. \n\t\t\tFor example, arn:partition:service:region:account-id:resource-type/resource-name/resource-id.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#QueryArn": { @@ -10232,36 +10620,85 @@ "target": "com.amazonaws.configservice#AllSupported", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Config records configuration changes for\n\t\t\tevery supported type of regional resource.

\n\t\t

If you set this option to true, when Config\n\t\t\tadds support for a new type of regional resource, it starts\n\t\t\trecording resources of that type automatically.

\n\t\t

If you set this option to true, you cannot\n\t\t\tenumerate a list of resourceTypes.

" + "smithy.api#documentation": "

Specifies whether Config records configuration changes for all supported regional resource types.

\n

If you set this field to true, when Config\n\t\t\tadds support for a new type of regional resource, Config starts recording resources of that type automatically.

\n

If you set this field to true,\n\t\t\tyou cannot enumerate specific resource types to record in the resourceTypes field of RecordingGroup, or to exclude in the resourceTypes field of ExclusionByResourceTypes.

" } }, "includeGlobalResourceTypes": { "target": "com.amazonaws.configservice#IncludeGlobalResourceTypes", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Config includes all supported types of\n\t\t\tglobal resources (for example, IAM resources) with the resources\n\t\t\tthat it records.

\n\t\t

Before you can set this option to true, you must\n\t\t\tset the allSupported option to\n\t\t\ttrue.

\n\t\t

If you set this option to true, when Config\n\t\t\tadds support for a new type of global resource, it starts recording\n\t\t\tresources of that type automatically.

\n\t\t

The configuration details for any global resource are the same\n\t\t\tin all regions. To prevent duplicate configuration items, you should\n\t\t\tconsider customizing Config in only one region to record global\n\t\t\tresources.

" + "smithy.api#documentation": "

Specifies whether Config records configuration changes for all supported global resources.

\n

Before you set this field to true,\n\t\t\tset the allSupported field of RecordingGroup to\n\t\t\ttrue. Optionally, you can set the useOnly field of RecordingStrategy to ALL_SUPPORTED_RESOURCE_TYPES.

\n

If you set this field to true, when Config\n\t\t\tadds support for a new type of global resource in the Region where you set up the configuration recorder, Config starts recording\n\t\t\tresources of that type automatically.

\n \n

If you set this field to false but list global resource types in the resourceTypes field of RecordingGroup,\n\t\t\tConfig will still record configuration changes for those specified resource types regardless of if you set the includeGlobalResourceTypes field to false.

\n

If you do not want to record configuration changes to global resource types, make sure to not list them in the resourceTypes field\n\t\t\tin addition to setting the includeGlobalResourceTypes field to false.

\n
" } }, "resourceTypes": { "target": "com.amazonaws.configservice#ResourceTypeList", "traits": { - "smithy.api#documentation": "

A comma-separated list that specifies the types of Amazon Web Services\n\t\t\tresources for which Config records configuration changes (for\n\t\t\texample, AWS::EC2::Instance or\n\t\t\t\tAWS::CloudTrail::Trail).

\n\t\t

To record all configuration changes, you must\n\t\t\tset the allSupported option to\n\t\t\ttrue.

\n\t\t

If you set this option to false, when Config\n\t\t\tadds support for a new type of resource, it will not record\n\t\t\tresources of that type unless you manually add that type to your\n\t\t\trecording group.

\n\t\t

For a list of valid resourceTypes values, see the\n\t\t\t\tresourceType Value column in\n\t\t\t\tSupported Amazon Web Services resource Types.

" + "smithy.api#documentation": "

A comma-separated list that specifies which resource types Config\n\t\t\trecords.

\n

Optionally, you can set the useOnly field of RecordingStrategy to INCLUSION_BY_RESOURCE_TYPES.

\n

To record all configuration changes,\n\t\t\t\tset the allSupported field of RecordingGroup to\n\t\t\t\ttrue, and either omit this field or don't specify any resource types in this field. If you set the allSupported field to false and specify values for resourceTypes,\n\t\t\t\t\twhen Config adds support for a new type of resource,\n\t\t\t\t\tit will not record resources of that type unless you manually add that type to your recording group.

\n

For a list of valid resourceTypes values, see the\n\t\t\t\tResource Type Value column in\n\t\t\t\tSupported Amazon Web Services resource Types in the Config developer guide.

\n \n

\n Region Availability\n

\n

Before specifying a resource type for Config to track,\n\t\t\t\tcheck Resource Coverage by Region Availability\n\t\t\t\tto see if the resource type is supported in the Amazon Web Services Region where you set up Config.\n\t\t\t\tIf a resource type is supported by Config in at least one Region,\n\t\t\t\tyou can enable the recording of that resource type in all Regions supported by Config,\n\t\t\t\teven if the specified resource type is not supported in the Amazon Web Services Region where you set up Config.

\n
" + } + }, + "exclusionByResourceTypes": { + "target": "com.amazonaws.configservice#ExclusionByResourceTypes", + "traits": { + "smithy.api#documentation": "

An object that specifies how Config excludes resource types from being recorded by the configuration recorder.

\n

To use this option, you must set the useOnly field of RecordingStrategy to EXCLUSION_BY_RESOURCE_TYPES.

" + } + }, + "recordingStrategy": { + "target": "com.amazonaws.configservice#RecordingStrategy", + "traits": { + "smithy.api#documentation": "

An object that specifies the recording strategy for the configuration recorder.

\n
    \n
  • \n

    If you set the useOnly field of RecordingStrategy to ALL_SUPPORTED_RESOURCE_TYPES, Config records configuration changes for all supported regional resource types. You also must set the allSupported field of RecordingGroup to true. When Config adds support for a new type of regional resource, Config automatically starts recording resources of that type.

    \n
  • \n
  • \n

    If you set the useOnly field of RecordingStrategy to INCLUSION_BY_RESOURCE_TYPES, Config records configuration changes for only the resource types you specify in the resourceTypes field of RecordingGroup.

    \n
  • \n
  • \n

    If you set the useOnly field of RecordingStrategy to EXCLUSION_BY_RESOURCE_TYPES, Config records configuration changes for all supported resource types\n\t\t\t\texcept the resource types that you specify as exemptions to exclude from being recorded in the resourceTypes field of ExclusionByResourceTypes.

    \n
  • \n
\n \n

The recordingStrategy field is optional when you set the\n\t\t\tallSupported field of RecordingGroup to true.

\n

The recordingStrategy field is optional when you list resource types in the\n\t\t\t\tresourceTypes field of RecordingGroup.

\n

The recordingStrategy field is required if you list resource types to exclude from recording in the resourceTypes field of ExclusionByResourceTypes.

\n
\n \n

If you choose EXCLUSION_BY_RESOURCE_TYPES for the recording strategy, the exclusionByResourceTypes field will override other properties in the request.

\n

For example, even if you set includeGlobalResourceTypes to false, global resource types will still be automatically\n\t\t\trecorded in this option unless those resource types are specifically listed as exemptions in the resourceTypes field of exclusionByResourceTypes.

\n

By default, if you choose the EXCLUSION_BY_RESOURCE_TYPES recording strategy,\n\t\t\t\twhen Config adds support for a new resource type in the Region where you set up the configuration recorder, including global resource types,\n\t\t\t\tConfig starts recording resources of that type automatically.

\n
" } } }, "traits": { - "smithy.api#documentation": "

Specifies which Amazon Web Services resource types Config\n\t\t\trecords for configuration changes. In the recording group, you specify whether you want to record all supported resource types\n\t\t\tor only specific types of resources.

\n\t\t\n\t \t

By default, Config records the configuration changes for all supported types of\n\t\t\t\tregional resources that Config discovers in the region in which it is\n\t\t\t\trunning. Regional resources are tied to a region and can be used only in that region. Examples\n\t\t\t\tof regional resources are EC2 instances and EBS volumes.

\n\t\t\t

You can also have Config record supported types of global resources.\n\t\t\t\tGlobal resources are not tied to a specific region and can be used in all regions. The global\n\t\t\t\tresource types that Config supports include IAM users, groups, roles, and customer managed\n\t\t\t\tpolicies.

\n\t\t\n\t\t \n\t\t\t

Global resource types onboarded to Config recording after February 2022 will only be\n\t\t\t\trecorded in the service's home region for the commercial partition and\n\t\t\t\tAmazon Web Services GovCloud (US) West for the GovCloud partition. You can view the Configuration Items for\n\t\t\t\tthese new global resource types only in their home region and Amazon Web Services GovCloud (US) West.

\n\t\t\t\n\t\t\t

Supported global resource types onboarded before February 2022 such as\n\t\t\t\tAWS::IAM::Group, AWS::IAM::Policy, AWS::IAM::Role,\n\t\t\t\tAWS::IAM::User remain unchanged, and they will continue to deliver\n\t\t\t\tConfiguration Items in all supported regions in Config. The change will only affect new global\n\t\t\t\tresource types onboarded after February 2022.

\n\t\t\t\n\t\t\t

To record global resource types onboarded after February 2022,\n\t\t\t\tenable All Supported Resource Types in the home region of the global resource type you want to record.

\t\n\t\t
\n\t\t\n\t\t

If you don't want Config to record all resources, you can\n\t\t\tspecify which types of resources it will record with the\n\t\t\t\tresourceTypes parameter.

\n\t\t

For a list of supported resource types, see Supported Resource Types.

\n\t\t

For more information and a table of the Home Regions for Global Resource Types Onboarded after February 2022, see Selecting Which Resources Config Records.

" + "smithy.api#documentation": "

Specifies which resource types Config\n\t\t\trecords for configuration changes.\n\t\t\tIn the recording group, you specify whether you want to record all supported resource types or to include or exclude specific types of resources.

\n

By default, Config records configuration changes for all supported types of\n\t\t\t\tRegional resources that Config discovers in the\n\t\t\t\tAmazon Web Services Region in which it is running. Regional resources are tied to a\n\t\t\tRegion and can be used only in that Region. Examples of Regional resources are Amazon EC2 instances and Amazon EBS volumes.

\n

You can also have Config record supported types of global resources.\n\t\t\t\tGlobal resources are not tied to a specific Region and can be used in all Regions. The global\n\t\t\t\tresource types that Config supports include IAM users, groups, roles, and customer managed\n\t\t\t\tpolicies.

\n \n

Global resource types onboarded to Config recording after February 2022 will\n\t\t\t\tbe recorded only in the service's home Region for the commercial partition and\n\t\t\t\tAmazon Web Services GovCloud (US-West) for the Amazon Web Services GovCloud (US) partition. You can view the\n\t\t\t\tConfiguration Items for these new global resource types only in their home Region\n\t\t\t\tand Amazon Web Services GovCloud (US-West).

\n
\n

If you don't want Config to record all resources, you can specify which types of resources Config records with the resourceTypes parameter.

\n

For a list of supported resource types, see Supported Resource Types in the Config developer guide.

\n

For more information and a table of the Home Regions for Global Resource Types Onboarded after February 2022, see Selecting Which Resources Config Records in the Config developer guide.

" } }, - "com.amazonaws.configservice#ReevaluateConfigRuleNames": { - "type": "list", - "member": { - "target": "com.amazonaws.configservice#ConfigRuleName" + "com.amazonaws.configservice#RecordingStrategy": { + "type": "structure", + "members": { + "useOnly": { + "target": "com.amazonaws.configservice#RecordingStrategyType", + "traits": { + "smithy.api#documentation": "

The recording strategy for the configuration recorder.

\n
    \n
  • \n

    If you set this option to ALL_SUPPORTED_RESOURCE_TYPES, Config records configuration changes for all supported regional resource types. You also must set the allSupported field of RecordingGroup to true.

    \n

    When Config adds support for a new type of regional resource, Config automatically starts recording resources of that type. For a list of supported resource types,\n\t\t\t\tsee Supported Resource Types in the Config developer guide.

    \n
  • \n
  • \n

    If you set this option to INCLUSION_BY_RESOURCE_TYPES, Config records\n\t\t\t\t\tconfiguration changes for only the resource types that you specify in the\n\t\t\t\t\t\tresourceTypes field of RecordingGroup.

    \n
  • \n
  • \n

    If you set this option to EXCLUSION_BY_RESOURCE_TYPES, Config records\n\t\t\t\t\tconfiguration changes for all supported resource types, except the resource\n\t\t\t\t\ttypes that you specify as exemptions to exclude from being recorded in the\n\t\t\t\t\t\tresourceTypes field of ExclusionByResourceTypes.

    \n
  • \n
\n \n

The recordingStrategy field is optional when you set the\n\t\t\tallSupported field of RecordingGroup to true.

\n

The recordingStrategy field is optional when you list resource types in the\n\t\t\t\tresourceTypes field of RecordingGroup.

\n

The recordingStrategy field is required if you list resource types to exclude from recording in the resourceTypes field of ExclusionByResourceTypes.

\n
\n \n

If you choose EXCLUSION_BY_RESOURCE_TYPES for the recording strategy, the exclusionByResourceTypes field will override other properties in the request.

\n

For example, even if you set includeGlobalResourceTypes to false, global resource types will still be automatically\n\t\t\trecorded in this option unless those resource types are specifically listed as exemptions in the resourceTypes field of exclusionByResourceTypes.

\n

By default, if you choose the EXCLUSION_BY_RESOURCE_TYPES recording strategy,\n\t\t\t\twhen Config adds support for a new resource type in the Region where you set up the configuration recorder, including global resource types,\n\t\t\t\tConfig starts recording resources of that type automatically.

\n
" + } + } }, "traits": { - "smithy.api#length": { - "min": 1, - "max": 25 + "smithy.api#documentation": "

Specifies the recording strategy of the configuration recorder.

" + } + }, + "com.amazonaws.configservice#RecordingStrategyType": { + "type": "enum", + "members": { + "ALL_SUPPORTED_RESOURCE_TYPES": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ALL_SUPPORTED_RESOURCE_TYPES" + } + }, + "INCLUSION_BY_RESOURCE_TYPES": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INCLUSION_BY_RESOURCE_TYPES" + } + }, + "EXCLUSION_BY_RESOURCE_TYPES": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "EXCLUSION_BY_RESOURCE_TYPES" + } + } + } + }, + "com.amazonaws.configservice#ReevaluateConfigRuleNames": { + "type": "list", + "member": { + "target": "com.amazonaws.configservice#ConfigRuleName" + }, + "traits": { + "smithy.api#length": { + "min": 1, + "max": 25 } } }, @@ -10335,14 +10772,14 @@ "TargetId": { "target": "com.amazonaws.configservice#StringWithCharLimit256", "traits": { - "smithy.api#documentation": "

Target ID is the name of the public document.

", + "smithy.api#documentation": "

Target ID is the name of the SSM document.

", "smithy.api#required": {} } }, "TargetVersion": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

Version of the target. For example, version of the SSM document.

\n\t\t \n

If you make backward incompatible changes to the SSM document, \n\t\t\tyou must call PutRemediationConfiguration API again to ensure the remediations can run.

\n
" + "smithy.api#documentation": "

Version of the target. For example, version of the SSM document.

\n \n

If you make backward incompatible changes to the SSM document, \n\t\t\tyou must call PutRemediationConfiguration API again to ensure the remediations can run.

\n
" } }, "Parameters": { @@ -10373,13 +10810,13 @@ "MaximumAutomaticAttempts": { "target": "com.amazonaws.configservice#AutoRemediationAttempts", "traits": { - "smithy.api#documentation": "

The maximum number of failed attempts for auto-remediation. If you do not select a number, the default is 5.

\n\t\t

For example, if you specify MaximumAutomaticAttempts as 5 with RetryAttemptSeconds as 50 seconds, \n\t\t\t\n\t\t\tConfig will put a RemediationException on your behalf for the failing resource after the 5th failed attempt within 50 seconds.

" + "smithy.api#documentation": "

The maximum number of failed attempts for auto-remediation. If you do not select a number, the default is 5.

\n

For example, if you specify MaximumAutomaticAttempts as 5 with RetryAttemptSeconds as 50 seconds, \n\t\t\t\n\t\t\tConfig will put a RemediationException on your behalf for the failing resource after the 5th failed attempt within 50 seconds.

" } }, "RetryAttemptSeconds": { "target": "com.amazonaws.configservice#AutoRemediationAttemptSeconds", "traits": { - "smithy.api#documentation": "

Maximum time in seconds that Config runs auto-remediation. If you do not select a number, the default is 60 seconds.

\n\t\t

For example, if you specify RetryAttemptSeconds as 50 seconds and MaximumAutomaticAttempts as 5, \n\t\tConfig will run auto-remediations 5 times within 50 seconds before throwing an exception.

" + "smithy.api#documentation": "

Maximum time in seconds that Config runs auto-remediation. If you do not select a number, the default is 60 seconds.

\n

For example, if you specify RetryAttemptSeconds as 50 seconds and MaximumAutomaticAttempts as 5, \n\t\tConfig will run auto-remediations 5 times within 50 seconds before throwing an exception.

" } }, "Arn": { @@ -10835,12 +11272,12 @@ "ResourceConfigurationSchemaType": { "target": "com.amazonaws.configservice#ResourceConfigurationSchemaType", "traits": { - "smithy.api#documentation": "

The schema type of the resource configuration.

" + "smithy.api#documentation": "

The schema type of the resource configuration.

\n \n

You can find the\n\t\t\tResource type schema, or CFN_RESOURCE_SCHEMA, in \"Amazon Web Services public extensions\" within the CloudFormation registry or with the following CLI commmand:\n\t\t\taws cloudformation describe-type --type-name \"AWS::S3::Bucket\" --type RESOURCE.

\n

For more information, see Managing extensions through the CloudFormation registry\n\t\t\t\tand Amazon Web Services resource and property types reference in the CloudFormation User Guide.

\n
" } } }, "traits": { - "smithy.api#documentation": "

Returns information about the resource being evaluated.

" + "smithy.api#documentation": "

Returns information about the resource being evaluated.

" } }, "com.amazonaws.configservice#ResourceEvaluation": { @@ -11041,7 +11478,7 @@ } }, "traits": { - "smithy.api#documentation": "

You see this exception in the following cases:

\n\t\t
    \n
  • \n

    For DeleteConfigRule, Config is deleting this rule. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConfigRule, the rule is deleting your evaluation results. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConfigRule, a remediation action is associated with the rule and Config cannot delete this rule. Delete the remediation action associated with the rule before deleting the rule and try your request again later.

    \n
  • \n
  • \n

    For PutConfigOrganizationRule, organization Config rule deletion is in progress. Try your request again later.

    \n
  • \n
  • \n

    For DeleteOrganizationConfigRule, organization Config rule creation is in progress. Try your request again later.

    \n
  • \n
  • \n

    For PutConformancePack and PutOrganizationConformancePack, a conformance pack creation, update, and deletion is in progress. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConformancePack, a conformance pack creation, update, and deletion is in progress. Try your request again later.

    \n
  • \n
", + "smithy.api#documentation": "

You see this exception in the following cases:

\n
    \n
  • \n

    For DeleteConfigRule, Config is deleting this rule. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConfigRule, the rule is deleting your evaluation results. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConfigRule, a remediation action is associated with the rule and Config cannot delete this rule. Delete the remediation action associated with the rule before deleting the rule and try your request again later.

    \n
  • \n
  • \n

    For PutConfigOrganizationRule, organization Config rule deletion is in progress. Try your request again later.

    \n
  • \n
  • \n

    For DeleteOrganizationConfigRule, organization Config rule creation is in progress. Try your request again later.

    \n
  • \n
  • \n

    For PutConformancePack and PutOrganizationConformancePack, a conformance pack creation, update, and deletion is in progress. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConformancePack, a conformance pack creation, update, and deletion is in progress. Try your request again later.

    \n
  • \n
", "smithy.api#error": "client" } }, @@ -11787,316 +12224,1114 @@ "smithy.api#enumValue": "AWS::Kinesis::StreamConsumer" } }, - "CodeDeployApplication": { + "CodeDeployApplication": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::CodeDeploy::Application" + } + }, + "CodeDeployDeploymentConfig": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::CodeDeploy::DeploymentConfig" + } + }, + "CodeDeployDeploymentGroup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::CodeDeploy::DeploymentGroup" + } + }, + "LaunchTemplate": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::LaunchTemplate" + } + }, + "ECRPublicRepository": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ECR::PublicRepository" + } + }, + "GuardDutyDetector": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GuardDuty::Detector" + } + }, + "EMRSecurityConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EMR::SecurityConfiguration" + } + }, + "SageMakerCodeRepository": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SageMaker::CodeRepository" + } + }, + "Route53ResolverResolverEndpoint": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53Resolver::ResolverEndpoint" + } + }, + "Route53ResolverResolverRule": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53Resolver::ResolverRule" + } + }, + "Route53ResolverResolverRuleAssociation": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53Resolver::ResolverRuleAssociation" + } + }, + "DMSReplicationSubnetGroup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DMS::ReplicationSubnetGroup" + } + }, + "DMSEventSubscription": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DMS::EventSubscription" + } + }, + "MSKCluster": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::MSK::Cluster" + } + }, + "StepFunctionsActivity": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::StepFunctions::Activity" + } + }, + "WorkSpacesWorkspace": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::WorkSpaces::Workspace" + } + }, + "WorkSpacesConnectionAlias": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::WorkSpaces::ConnectionAlias" + } + }, + "SageMakerModel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SageMaker::Model" + } + }, + "ListenerV2": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ElasticLoadBalancingV2::Listener" + } + }, + "StepFunctionsStateMachine": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::StepFunctions::StateMachine" + } + }, + "BatchJobQueue": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Batch::JobQueue" + } + }, + "BatchComputeEnvironment": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Batch::ComputeEnvironment" + } + }, + "AccessAnalyzerAnalyzer": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AccessAnalyzer::Analyzer" + } + }, + "AthenaWorkGroup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Athena::WorkGroup" + } + }, + "AthenaDataCatalog": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Athena::DataCatalog" + } + }, + "DetectiveGraph": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Detective::Graph" + } + }, + "GlobalAcceleratorAccelerator": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GlobalAccelerator::Accelerator" + } + }, + "GlobalAcceleratorEndpointGroup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GlobalAccelerator::EndpointGroup" + } + }, + "GlobalAcceleratorListener": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GlobalAccelerator::Listener" + } + }, + "TransitGatewayAttachment": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::TransitGatewayAttachment" + } + }, + "TransitGatewayRouteTable": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::TransitGatewayRouteTable" + } + }, + "DMSCertificate": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DMS::Certificate" + } + }, + "AppConfigApplication": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppConfig::Application" + } + }, + "AppSyncGraphQLApi": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppSync::GraphQLApi" + } + }, + "DataSyncLocationSMB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationSMB" + } + }, + "DataSyncLocationFSxLustre": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationFSxLustre" + } + }, + "DataSyncLocationS3": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationS3" + } + }, + "DataSyncLocationEFS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationEFS" + } + }, + "DataSyncTask": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::Task" + } + }, + "DataSyncLocationNFS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationNFS" + } + }, + "NetworkInsightsAccessScopeAnalysis": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::NetworkInsightsAccessScopeAnalysis" + } + }, + "EKSFargateProfile": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EKS::FargateProfile" + } + }, + "GlueJob": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Glue::Job" + } + }, + "GuardDutyThreatIntelSet": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GuardDuty::ThreatIntelSet" + } + }, + "GuardDutyIPSet": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GuardDuty::IPSet" + } + }, + "SageMakerWorkteam": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SageMaker::Workteam" + } + }, + "SageMakerNotebookInstanceLifecycleConfig": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SageMaker::NotebookInstanceLifecycleConfig" + } + }, + "ServiceDiscoveryService": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ServiceDiscovery::Service" + } + }, + "ServiceDiscoveryPublicDnsNamespace": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ServiceDiscovery::PublicDnsNamespace" + } + }, + "SESContactList": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SES::ContactList" + } + }, + "SESConfigurationSet": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SES::ConfigurationSet" + } + }, + "Route53HostedZone": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53::HostedZone" + } + }, + "IoTEventsInput": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTEvents::Input" + } + }, + "IoTEventsDetectorModel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTEvents::DetectorModel" + } + }, + "IoTEventsAlarmModel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTEvents::AlarmModel" + } + }, + "ServiceDiscoveryHttpNamespace": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ServiceDiscovery::HttpNamespace" + } + }, + "EventsEventBus": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::EventBus" + } + }, + "ImageBuilderContainerRecipe": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ImageBuilder::ContainerRecipe" + } + }, + "ImageBuilderDistributionConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ImageBuilder::DistributionConfiguration" + } + }, + "ImageBuilderInfrastructureConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ImageBuilder::InfrastructureConfiguration" + } + }, + "DataSyncLocationObjectStorage": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationObjectStorage" + } + }, + "DataSyncLocationHDFS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationHDFS" + } + }, + "GlueClassifier": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Glue::Classifier" + } + }, + "Route53RecoveryReadinessCell": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryReadiness::Cell" + } + }, + "Route53RecoveryReadinessReadinessCheck": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryReadiness::ReadinessCheck" + } + }, + "ECRRegistryPolicy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ECR::RegistryPolicy" + } + }, + "BackupReportPlan": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Backup::ReportPlan" + } + }, + "LightsailCertificate": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lightsail::Certificate" + } + }, + "RUMAppMonitor": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::RUM::AppMonitor" + } + }, + "EventsEndpoint": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::Endpoint" + } + }, + "SESReceiptRuleSet": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SES::ReceiptRuleSet" + } + }, + "EventsArchive": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::Archive" + } + }, + "EventsApiDestination": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::ApiDestination" + } + }, + "LightsailDisk": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lightsail::Disk" + } + }, + "FISExperimentTemplate": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FIS::ExperimentTemplate" + } + }, + "DataSyncLocationFSxWindows": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationFSxWindows" + } + }, + "SESReceiptFilter": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SES::ReceiptFilter" + } + }, + "GuardDutyFilter": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GuardDuty::Filter" + } + }, + "SESTemplate": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SES::Template" + } + }, + "AmazonMQBroker": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AmazonMQ::Broker" + } + }, + "AppConfigEnvironment": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppConfig::Environment" + } + }, + "AppConfigConfigurationProfile": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppConfig::ConfigurationProfile" + } + }, + "Cloud9EnvironmentEC2": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Cloud9::EnvironmentEC2" + } + }, + "EventSchemasRegistry": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EventSchemas::Registry" + } + }, + "EventSchemasRegistryPolicy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EventSchemas::RegistryPolicy" + } + }, + "EventSchemasDiscoverer": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EventSchemas::Discoverer" + } + }, + "FraudDetectorLabel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FraudDetector::Label" + } + }, + "FraudDetectorEntityType": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FraudDetector::EntityType" + } + }, + "FraudDetectorVariable": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FraudDetector::Variable" + } + }, + "FraudDetectorOutcome": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FraudDetector::Outcome" + } + }, + "IoTAuthorizer": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::Authorizer" + } + }, + "IoTSecurityProfile": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::SecurityProfile" + } + }, + "IoTRoleAlias": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::RoleAlias" + } + }, + "IoTDimension": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::Dimension" + } + }, + "IoTAnalyticsDatastore": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTAnalytics::Datastore" + } + }, + "LightsailBucket": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lightsail::Bucket" + } + }, + "LightsailStaticIp": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lightsail::StaticIp" + } + }, + "MediaPackagePackagingGroup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::MediaPackage::PackagingGroup" + } + }, + "Route53RecoveryReadinessRecoveryGroup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryReadiness::RecoveryGroup" + } + }, + "ResilienceHubResiliencyPolicy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ResilienceHub::ResiliencyPolicy" + } + }, + "TransferWorkflow": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Transfer::Workflow" + } + }, + "EKSIdentityProviderConfig": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EKS::IdentityProviderConfig" + } + }, + "EKSAddon": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EKS::Addon" + } + }, + "GlueMLTransform": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Glue::MLTransform" + } + }, + "IoTPolicy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::Policy" + } + }, + "IoTMitigationAction": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::MitigationAction" + } + }, + "IoTTwinMakerWorkspace": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTTwinMaker::Workspace" + } + }, + "IoTTwinMakerEntity": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTTwinMaker::Entity" + } + }, + "IoTAnalyticsDataset": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTAnalytics::Dataset" + } + }, + "IoTAnalyticsPipeline": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTAnalytics::Pipeline" + } + }, + "IoTAnalyticsChannel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTAnalytics::Channel" + } + }, + "IoTSiteWiseDashboard": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::Dashboard" + } + }, + "IoTSiteWiseProject": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::Project" + } + }, + "IoTSiteWisePortal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::Portal" + } + }, + "IoTSiteWiseAssetModel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::AssetModel" + } + }, + "IVSChannel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IVS::Channel" + } + }, + "IVSRecordingConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IVS::RecordingConfiguration" + } + }, + "IVSPlaybackKeyPair": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IVS::PlaybackKeyPair" + } + }, + "KinesisAnalyticsV2Application": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::KinesisAnalyticsV2::Application" + } + }, + "RDSGlobalCluster": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::RDS::GlobalCluster" + } + }, + "S3MultiRegionAccessPoint": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::S3::MultiRegionAccessPoint" + } + }, + "DeviceFarmTestGridProject": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DeviceFarm::TestGridProject" + } + }, + "BudgetsBudgetsAction": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Budgets::BudgetsAction" + } + }, + "LexBot": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lex::Bot" + } + }, + "CodeGuruReviewerRepositoryAssociation": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::CodeGuruReviewer::RepositoryAssociation" + } + }, + "IoTCustomMetric": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::CustomMetric" + } + }, + "Route53ResolverFirewallDomainList": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53Resolver::FirewallDomainList" + } + }, + "RoboMakerRobotApplicationVersion": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::RoboMaker::RobotApplicationVersion" + } + }, + "EC2TrafficMirrorSession": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::TrafficMirrorSession" + } + }, + "IoTSiteWiseGateway": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::Gateway" + } + }, + "LexBotAlias": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lex::BotAlias" + } + }, + "LookoutMetricsAlert": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::LookoutMetrics::Alert" + } + }, + "IoTAccountAuditConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::AccountAuditConfiguration" + } + }, + "EC2TrafficMirrorTarget": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::CodeDeploy::Application" + "smithy.api#enumValue": "AWS::EC2::TrafficMirrorTarget" } }, - "CodeDeployDeploymentConfig": { + "S3StorageLens": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::CodeDeploy::DeploymentConfig" + "smithy.api#enumValue": "AWS::S3::StorageLens" } }, - "CodeDeployDeploymentGroup": { + "IoTScheduledAudit": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::CodeDeploy::DeploymentGroup" + "smithy.api#enumValue": "AWS::IoT::ScheduledAudit" } }, - "LaunchTemplate": { + "EventsConnection": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::EC2::LaunchTemplate" + "smithy.api#enumValue": "AWS::Events::Connection" } }, - "ECRPublicRepository": { + "EventSchemasSchema": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::ECR::PublicRepository" + "smithy.api#enumValue": "AWS::EventSchemas::Schema" } }, - "GuardDutyDetector": { + "MediaPackagePackagingConfiguration": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::GuardDuty::Detector" + "smithy.api#enumValue": "AWS::MediaPackage::PackagingConfiguration" } }, - "EMRSecurityConfiguration": { + "KinesisVideoSignalingChannel": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::EMR::SecurityConfiguration" + "smithy.api#enumValue": "AWS::KinesisVideo::SignalingChannel" } }, - "SageMakerCodeRepository": { + "AppStreamDirectoryConfig": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::SageMaker::CodeRepository" + "smithy.api#enumValue": "AWS::AppStream::DirectoryConfig" } }, - "Route53ResolverResolverEndpoint": { + "LookoutVisionProject": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Route53Resolver::ResolverEndpoint" + "smithy.api#enumValue": "AWS::LookoutVision::Project" } }, - "Route53ResolverResolverRule": { + "Route53RecoveryControlCluster": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Route53Resolver::ResolverRule" + "smithy.api#enumValue": "AWS::Route53RecoveryControl::Cluster" } }, - "Route53ResolverResolverRuleAssociation": { + "Route53RecoveryControlSafetyRule": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Route53Resolver::ResolverRuleAssociation" + "smithy.api#enumValue": "AWS::Route53RecoveryControl::SafetyRule" } }, - "DMSReplicationSubnetGroup": { + "Route53RecoveryControlControlPanel": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DMS::ReplicationSubnetGroup" + "smithy.api#enumValue": "AWS::Route53RecoveryControl::ControlPanel" } }, - "DMSEventSubscription": { + "Route53RecoveryControlRoutingControl": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DMS::EventSubscription" + "smithy.api#enumValue": "AWS::Route53RecoveryControl::RoutingControl" } }, - "MSKCluster": { + "Route53RecoveryReadinessResourceSet": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::MSK::Cluster" + "smithy.api#enumValue": "AWS::Route53RecoveryReadiness::ResourceSet" } }, - "StepFunctionsActivity": { + "RoboMakerSimulationApplication": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::StepFunctions::Activity" + "smithy.api#enumValue": "AWS::RoboMaker::SimulationApplication" } }, - "WorkSpacesWorkspace": { + "RoboMakerRobotApplication": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::WorkSpaces::Workspace" + "smithy.api#enumValue": "AWS::RoboMaker::RobotApplication" } }, - "WorkSpacesConnectionAlias": { + "HealthLakeFHIRDatastore": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::WorkSpaces::ConnectionAlias" + "smithy.api#enumValue": "AWS::HealthLake::FHIRDatastore" } }, - "SageMakerModel": { + "PinpointSegment": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::SageMaker::Model" + "smithy.api#enumValue": "AWS::Pinpoint::Segment" } }, - "ListenerV2": { + "PinpointApplicationSettings": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::ElasticLoadBalancingV2::Listener" + "smithy.api#enumValue": "AWS::Pinpoint::ApplicationSettings" } }, - "StepFunctionsStateMachine": { + "EventsRule": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::StepFunctions::StateMachine" + "smithy.api#enumValue": "AWS::Events::Rule" } }, - "BatchJobQueue": { + "EC2DHCPOptions": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Batch::JobQueue" + "smithy.api#enumValue": "AWS::EC2::DHCPOptions" } }, - "BatchComputeEnvironment": { + "EC2NetworkInsightsPath": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Batch::ComputeEnvironment" + "smithy.api#enumValue": "AWS::EC2::NetworkInsightsPath" } }, - "AccessAnalyzerAnalyzer": { + "EC2TrafficMirrorFilter": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::AccessAnalyzer::Analyzer" + "smithy.api#enumValue": "AWS::EC2::TrafficMirrorFilter" } }, - "AthenaWorkGroup": { + "EC2IPAM": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Athena::WorkGroup" + "smithy.api#enumValue": "AWS::EC2::IPAM" } }, - "AthenaDataCatalog": { + "IoTTwinMakerScene": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Athena::DataCatalog" + "smithy.api#enumValue": "AWS::IoTTwinMaker::Scene" } }, - "DetectiveGraph": { + "NetworkManagerTransitGatewayRegistration": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Detective::Graph" + "smithy.api#enumValue": "AWS::NetworkManager::TransitGatewayRegistration" } }, - "GlobalAcceleratorAccelerator": { + "CustomerProfilesDomain": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::GlobalAccelerator::Accelerator" + "smithy.api#enumValue": "AWS::CustomerProfiles::Domain" } }, - "GlobalAcceleratorEndpointGroup": { + "AutoScalingWarmPool": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::GlobalAccelerator::EndpointGroup" + "smithy.api#enumValue": "AWS::AutoScaling::WarmPool" } }, - "GlobalAcceleratorListener": { + "ConnectPhoneNumber": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::GlobalAccelerator::Listener" + "smithy.api#enumValue": "AWS::Connect::PhoneNumber" } }, - "TransitGatewayAttachment": { + "AppConfigDeploymentStrategy": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::EC2::TransitGatewayAttachment" + "smithy.api#enumValue": "AWS::AppConfig::DeploymentStrategy" } }, - "TransitGatewayRouteTable": { + "AppFlowFlow": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::EC2::TransitGatewayRouteTable" + "smithy.api#enumValue": "AWS::AppFlow::Flow" } }, - "DMSCertificate": { + "AuditManagerAssessment": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DMS::Certificate" + "smithy.api#enumValue": "AWS::AuditManager::Assessment" } }, - "AppConfigApplication": { + "CloudWatchMetricStream": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::AppConfig::Application" + "smithy.api#enumValue": "AWS::CloudWatch::MetricStream" } }, - "AppSyncGraphQLApi": { + "DeviceFarmInstanceProfile": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::AppSync::GraphQLApi" + "smithy.api#enumValue": "AWS::DeviceFarm::InstanceProfile" } }, - "DataSyncLocationSMB": { + "DeviceFarmProject": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DataSync::LocationSMB" + "smithy.api#enumValue": "AWS::DeviceFarm::Project" } }, - "DataSyncLocationFSxLustre": { + "EC2EC2Fleet": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DataSync::LocationFSxLustre" + "smithy.api#enumValue": "AWS::EC2::EC2Fleet" } }, - "DataSyncLocationS3": { + "EC2SubnetRouteTableAssociation": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DataSync::LocationS3" + "smithy.api#enumValue": "AWS::EC2::SubnetRouteTableAssociation" } }, - "DataSyncLocationEFS": { + "ECRPullThroughCacheRule": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DataSync::LocationEFS" + "smithy.api#enumValue": "AWS::ECR::PullThroughCacheRule" } }, - "DataSyncTask": { + "GroundStationConfig": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DataSync::Task" + "smithy.api#enumValue": "AWS::GroundStation::Config" } }, - "DataSyncLocationNFS": { + "ImageBuilderImagePipeline": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::DataSync::LocationNFS" + "smithy.api#enumValue": "AWS::ImageBuilder::ImagePipeline" } }, - "NetworkInsightsAccessScopeAnalysis": { + "IoTFleetMetric": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::EC2::NetworkInsightsAccessScopeAnalysis" + "smithy.api#enumValue": "AWS::IoT::FleetMetric" } }, - "EKSFargateProfile": { + "IoTWirelessServiceProfile": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::EKS::FargateProfile" + "smithy.api#enumValue": "AWS::IoTWireless::ServiceProfile" } }, - "GlueJob": { + "NetworkManagerDevice": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Glue::Job" + "smithy.api#enumValue": "AWS::NetworkManager::Device" } }, - "GuardDutyThreatIntelSet": { + "NetworkManagerGlobalNetwork": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::GuardDuty::ThreatIntelSet" + "smithy.api#enumValue": "AWS::NetworkManager::GlobalNetwork" } }, - "GuardDutyIPSet": { + "NetworkManagerLink": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::GuardDuty::IPSet" + "smithy.api#enumValue": "AWS::NetworkManager::Link" } }, - "SageMakerWorkteam": { + "NetworkManagerSite": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::SageMaker::Workteam" + "smithy.api#enumValue": "AWS::NetworkManager::Site" } }, - "SageMakerNotebookInstanceLifecycleConfig": { + "PanoramaPackage": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::SageMaker::NotebookInstanceLifecycleConfig" + "smithy.api#enumValue": "AWS::Panorama::Package" } }, - "ServiceDiscoveryService": { + "PinpointApp": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::ServiceDiscovery::Service" + "smithy.api#enumValue": "AWS::Pinpoint::App" } }, - "ServiceDiscoveryPublicDnsNamespace": { + "RedshiftScheduledAction": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::ServiceDiscovery::PublicDnsNamespace" + "smithy.api#enumValue": "AWS::Redshift::ScheduledAction" } }, - "SESContactList": { + "Route53ResolverFirewallRuleGroupAssociation": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::SES::ContactList" + "smithy.api#enumValue": "AWS::Route53Resolver::FirewallRuleGroupAssociation" } }, - "SESConfigurationSet": { + "SageMakerAppImageConfig": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::SES::ConfigurationSet" + "smithy.api#enumValue": "AWS::SageMaker::AppImageConfig" } }, - "Route53HostedZone": { + "SageMakerImage": { "target": "smithy.api#Unit", "traits": { - "smithy.api#enumValue": "AWS::Route53::HostedZone" + "smithy.api#enumValue": "AWS::SageMaker::Image" } } } @@ -12186,7 +13421,7 @@ "target": "com.amazonaws.configservice#RetentionPeriodInDays", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Number of days Config stores your historical information.

\n\t\t \n

Currently, only applicable to the configuration item history.

\n
", + "smithy.api#documentation": "

Number of days Config stores your historical information.

\n \n

Currently, only applicable to the configuration item history.

\n
", "smithy.api#required": {} } } @@ -12320,7 +13555,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command and an aggregator to query configuration state of Amazon Web Services resources across multiple accounts and regions, \n\t\t\tperforms the corresponding search, and returns resource configurations matching the properties.

\n\t\t

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

\n\t\t\n\t\t \n\t\t\t

If you run an aggregation query (i.e., using GROUP BY or using aggregate functions such as COUNT; e.g., SELECT resourceId, COUNT(*) WHERE resourceType = 'AWS::IAM::Role' GROUP BY resourceId)\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 500.

\n\t\t\t\n\t\t\t

If you run a non-aggregation query (i.e., not using GROUP BY or aggregate function; e.g., SELECT * WHERE resourceType = 'AWS::IAM::Role')\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 25.

\n\t\t
", + "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command and an aggregator to query configuration state of Amazon Web Services resources across multiple accounts and regions, \n\t\t\tperforms the corresponding search, and returns resource configurations matching the properties.

\n

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

\n \n

If you run an aggregation query (i.e., using GROUP BY or using aggregate functions such as COUNT; e.g., SELECT resourceId, COUNT(*) WHERE resourceType = 'AWS::IAM::Role' GROUP BY resourceId)\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 500.

\n

If you run a non-aggregation query (i.e., not using GROUP BY or aggregate function; e.g., SELECT * WHERE resourceType = 'AWS::IAM::Role')\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 25.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -12366,6 +13601,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#SelectAggregateResourceConfigResponse": { @@ -12386,6 +13624,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#SelectResourceConfig": { @@ -12408,7 +13649,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command, performs the corresponding search, and returns resource configurations matching the properties.

\n\t\t

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

", + "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command, performs the corresponding search, and returns resource configurations matching the properties.

\n

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -12440,6 +13681,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#SelectResourceConfigResponse": { @@ -12463,6 +13707,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#SortBy": { @@ -12499,20 +13746,20 @@ "Owner": { "target": "com.amazonaws.configservice#Owner", "traits": { - "smithy.api#documentation": "

Indicates whether Amazon Web Services or the customer owns and manages the Config rule.

\n\t\t\n\t\t

Config Managed Rules are predefined rules owned by Amazon Web Services. For more information, see Config Managed Rules in the Config developer guide.

\n\t\t\n\t\t

Config Custom Rules are rules that you can develop either with Guard (CUSTOM_POLICY) or Lambda (CUSTOM_LAMBDA). For more information, see Config Custom Rules in the Config developer guide.

", + "smithy.api#documentation": "

Indicates whether Amazon Web Services or the customer owns and manages the Config rule.

\n

Config Managed Rules are predefined rules owned by Amazon Web Services. For more information, see Config Managed Rules in the Config developer guide.

\n

Config Custom Rules are rules that you can develop either with Guard (CUSTOM_POLICY) or Lambda (CUSTOM_LAMBDA). For more information, see Config Custom Rules in the Config developer guide.

", "smithy.api#required": {} } }, "SourceIdentifier": { "target": "com.amazonaws.configservice#StringWithCharLimit256", "traits": { - "smithy.api#documentation": "

For Config Managed rules, a predefined identifier from a\n\t\t\tlist. For example, IAM_PASSWORD_POLICY is a managed\n\t\t\trule. To reference a managed rule, see List of Config Managed Rules.

\n\t\t

For Config Custom Lambda rules, the identifier is the Amazon Resource Name\n\t\t\t(ARN) of the rule's Lambda function, such as\n\t\t\tarn:aws:lambda:us-east-2:123456789012:function:custom_rule_name.

\n\t\t\n\t\t

For Config Custom Policy rules, this field will be ignored.

" + "smithy.api#documentation": "

For Config Managed rules, a predefined identifier from a\n\t\t\tlist. For example, IAM_PASSWORD_POLICY is a managed\n\t\t\trule. To reference a managed rule, see List of Config Managed Rules.

\n

For Config Custom Lambda rules, the identifier is the Amazon Resource Name\n\t\t\t(ARN) of the rule's Lambda function, such as\n\t\t\tarn:aws:lambda:us-east-2:123456789012:function:custom_rule_name.

\n

For Config Custom Policy rules, this field will be ignored.

" } }, "SourceDetails": { "target": "com.amazonaws.configservice#SourceDetails", "traits": { - "smithy.api#documentation": "

Provides the source and the message types that cause Config to evaluate your Amazon Web Services resources against a rule. It also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

\n\t\t\n\t\t

If the owner is set to CUSTOM_POLICY, the only acceptable values for the Config rule trigger message type are ConfigurationItemChangeNotification and OversizedConfigurationItemChangeNotification.

" + "smithy.api#documentation": "

Provides the source and the message types that cause Config to evaluate your Amazon Web Services resources against a rule. It also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

\n

If the owner is set to CUSTOM_POLICY, the only acceptable values for the Config rule trigger message type are ConfigurationItemChangeNotification and OversizedConfigurationItemChangeNotification.

" } }, "CustomPolicyDetails": { @@ -12538,13 +13785,13 @@ "MessageType": { "target": "com.amazonaws.configservice#MessageType", "traits": { - "smithy.api#documentation": "

The type of notification that triggers Config to run an\n\t\t\tevaluation for a rule. You can specify the following notification\n\t\t\ttypes:

\n\n\n\t\t
    \n
  • \n\t\t\t\t

    \n\t\t\t\t\t ConfigurationItemChangeNotification - Triggers\n\t\t\t\t\tan evaluation when Config delivers a configuration item\n\t\t\t\t\tas a result of a resource change.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    \n OversizedConfigurationItemChangeNotification\n\t\t\t\t\t- Triggers an evaluation when Config delivers an\n\t\t\t\t\toversized configuration item. Config may generate this\n\t\t\t\t\tnotification type when a resource changes and the\n\t\t\t\t\tnotification exceeds the maximum size allowed by Amazon\n\t\t\t\t\tSNS.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    \n ScheduledNotification - Triggers a\n\t\t\t\t\tperiodic evaluation at the frequency specified for\n\t\t\t\t\t\tMaximumExecutionFrequency.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    \n ConfigurationSnapshotDeliveryCompleted -\n\t\t\t\t\tTriggers a periodic evaluation when Config delivers a\n\t\t\t\t\tconfiguration snapshot.

    \n\t\t\t
  • \n
\n\n\t\t

If you want your custom rule to be triggered by configuration\n\t\t\tchanges, specify two SourceDetail objects, one for\n\t\t\t\tConfigurationItemChangeNotification and one for\n\t\t\t\tOversizedConfigurationItemChangeNotification.

" + "smithy.api#documentation": "

The type of notification that triggers Config to run an\n\t\t\tevaluation for a rule. You can specify the following notification\n\t\t\ttypes:

\n
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers\n\t\t\t\t\tan evaluation when Config delivers a configuration item\n\t\t\t\t\tas a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification\n\t\t\t\t\t- Triggers an evaluation when Config delivers an\n\t\t\t\t\toversized configuration item. Config may generate this\n\t\t\t\t\tnotification type when a resource changes and the\n\t\t\t\t\tnotification exceeds the maximum size allowed by Amazon\n\t\t\t\t\tSNS.

    \n
  • \n
  • \n

    \n ScheduledNotification - Triggers a\n\t\t\t\t\tperiodic evaluation at the frequency specified for\n\t\t\t\t\t\tMaximumExecutionFrequency.

    \n
  • \n
  • \n

    \n ConfigurationSnapshotDeliveryCompleted -\n\t\t\t\t\tTriggers a periodic evaluation when Config delivers a\n\t\t\t\t\tconfiguration snapshot.

    \n
  • \n
\n

If you want your custom rule to be triggered by configuration\n\t\t\tchanges, specify two SourceDetail objects, one for\n\t\t\t\tConfigurationItemChangeNotification and one for\n\t\t\t\tOversizedConfigurationItemChangeNotification.

" } }, "MaximumExecutionFrequency": { "target": "com.amazonaws.configservice#MaximumExecutionFrequency", "traits": { - "smithy.api#documentation": "

The frequency at which you want Config to run evaluations\n\t\t\tfor a custom rule with a periodic trigger. If you specify a value\n\t\t\tfor MaximumExecutionFrequency, then\n\t\t\t\tMessageType must use the\n\t\t\t\tScheduledNotification value.

\n\n\n\t\t\n\n\t\t \n\t\t\t

By default, rules with a periodic trigger are evaluated\n\t\t\t\tevery 24 hours. To change the frequency, specify a valid value\n\t\t\t\tfor the MaximumExecutionFrequency\n\t\t\t\tparameter.

\n\t\t\t

Based on the valid value you choose, Config runs\n\t\t\t\tevaluations once for each valid value. For example, if you\n\t\t\t\tchoose Three_Hours, Config runs evaluations\n\t\t\t\tonce every three hours. In this case, Three_Hours\n\t\t\t\tis the frequency of this rule.

\n\t\t
" + "smithy.api#documentation": "

The frequency at which you want Config to run evaluations\n\t\t\tfor a custom rule with a periodic trigger. If you specify a value\n\t\t\tfor MaximumExecutionFrequency, then\n\t\t\t\tMessageType must use the\n\t\t\t\tScheduledNotification value.

\n \n

By default, rules with a periodic trigger are evaluated\n\t\t\t\tevery 24 hours. To change the frequency, specify a valid value\n\t\t\t\tfor the MaximumExecutionFrequency\n\t\t\t\tparameter.

\n

Based on the valid value you choose, Config runs\n\t\t\t\tevaluations once for each valid value. For example, if you\n\t\t\t\tchoose Three_Hours, Config runs evaluations\n\t\t\t\tonce every three hours. In this case, Three_Hours\n\t\t\t\tis the frequency of this rule.

\n
" } } }, @@ -12886,7 +14133,7 @@ "name": "config" }, "aws.protocols#awsJson1_1": {}, - "smithy.api#documentation": "Config\n\n\t\t

Config provides a way to keep track of the configurations\n\t\t\tof all the Amazon Web Services resources associated with your Amazon Web Services account. You can\n\t\t\tuse Config to get the current and historical configurations of\n\t\t\teach Amazon Web Services resource and also to get information about the relationship\n\t\t\tbetween the resources. An Amazon Web Services resource can be an Amazon Compute\n\t\t\tCloud (Amazon EC2) instance, an Elastic Block Store (EBS) volume, an\n\t\t\telastic network Interface (ENI), or a security group. For a complete\n\t\t\tlist of resources currently supported by Config, see Supported Amazon Web Services resources.

\n\n\t\t

You can access and manage Config through the Amazon Web Services Management\n\t\t\tConsole, the Amazon Web Services Command Line Interface (Amazon Web Services CLI), the Config\n\t\t\tAPI, or the Amazon Web Services SDKs for Config. This reference guide contains\n\t\t\tdocumentation for the Config API and the Amazon Web Services CLI commands that\n\t\t\tyou can use to manage Config. The Config API uses the\n\t\t\tSignature Version 4 protocol for signing requests. For more\n\t\t\tinformation about how to sign a request with this protocol, see\n\t\t\t\tSignature\n\t\t\t\tVersion 4 Signing Process. For detailed information\n\t\t\tabout Config features and their associated actions or commands,\n\t\t\tas well as how to work with Amazon Web Services Management Console, see What Is Config in the Config Developer\n\t\t\t\tGuide.

", + "smithy.api#documentation": "Config\n

Config provides a way to keep track of the configurations\n\t\t\tof all the Amazon Web Services resources associated with your Amazon Web Services account. You can\n\t\t\tuse Config to get the current and historical configurations of\n\t\t\teach Amazon Web Services resource and also to get information about the relationship\n\t\t\tbetween the resources. An Amazon Web Services resource can be an Amazon Compute\n\t\t\tCloud (Amazon EC2) instance, an Elastic Block Store (EBS) volume, an\n\t\t\telastic network Interface (ENI), or a security group. For a complete\n\t\t\tlist of resources currently supported by Config, see Supported Amazon Web Services resources.

\n

You can access and manage Config through the Amazon Web Services Management\n\t\t\tConsole, the Amazon Web Services Command Line Interface (Amazon Web Services CLI), the Config\n\t\t\tAPI, or the Amazon Web Services SDKs for Config. This reference guide contains\n\t\t\tdocumentation for the Config API and the Amazon Web Services CLI commands that\n\t\t\tyou can use to manage Config. The Config API uses the\n\t\t\tSignature Version 4 protocol for signing requests. For more\n\t\t\tinformation about how to sign a request with this protocol, see\n\t\t\t\tSignature\n\t\t\t\tVersion 4 Signing Process. For detailed information\n\t\t\tabout Config features and their associated actions or commands,\n\t\t\tas well as how to work with Amazon Web Services Management Console, see What Is Config in the Config Developer\n\t\t\t\tGuide.

", "smithy.api#title": "AWS Config", "smithy.api#xmlNamespace": { "uri": "http://config.amazonaws.com/doc/2014-11-12/" @@ -13277,8 +14524,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13290,8 +14537,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13303,8 +14550,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13316,8 +14563,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13329,8 +14576,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13342,8 +14589,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13355,8 +14602,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13368,8 +14615,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13381,8 +14628,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13394,8 +14641,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13407,8 +14654,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13420,8 +14667,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13433,8 +14680,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13446,8 +14693,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13459,8 +14706,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13472,8 +14719,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13485,8 +14732,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13498,8 +14745,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13511,8 +14758,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13524,8 +14771,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -13537,8 +14784,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13550,8 +14797,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -13563,8 +14810,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13576,8 +14823,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -13589,8 +14836,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13602,8 +14849,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -13615,8 +14862,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -13628,8 +14875,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -13641,8 +14888,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13654,8 +14901,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13667,8 +14914,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -13680,8 +14927,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -13693,8 +14940,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -13706,8 +14953,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13719,8 +14966,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -13732,8 +14979,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13745,8 +14992,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -13758,8 +15005,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -13771,8 +15018,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -13784,8 +15031,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -13797,8 +15044,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -13810,8 +15068,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -13823,8 +15092,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -13836,8 +15116,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -13849,8 +15140,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -13862,8 +15153,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -13874,8 +15165,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -13886,10 +15177,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -13919,7 +15216,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs an on-demand evaluation for the specified Config rules\n\t\t\tagainst the last known configuration state of the resources. Use\n\t\t\t\tStartConfigRulesEvaluation when you want to test\n\t\t\tthat a rule you updated is working as expected.\n\t\t\t\tStartConfigRulesEvaluation does not re-record the\n\t\t\tlatest configuration state for your resources. It re-runs an\n\t\t\tevaluation against the last known state of your resources.

\n\t\t

You can specify up to 25 Config rules per request.

\n\n\t\t\n\t\t

An existing StartConfigRulesEvaluation call for\n\t\t\tthe specified rules must complete before you can call the API again.\n\t\t\tIf you chose to have Config stream to an Amazon SNS topic, you\n\t\t\twill receive a ConfigRuleEvaluationStarted notification\n\t\t\twhen the evaluation starts.

\n\t\t \n\t\t\t

You don't need to call the\n\t\t\t\t\tStartConfigRulesEvaluation API to run an\n\t\t\t\tevaluation for a new rule. When you create a rule, Config\n\t\t\t\tevaluates your resources against the rule automatically.\n\t\t\t

\n\t\t
\n\t\t

The StartConfigRulesEvaluation API is useful if\n\t\t\tyou want to run on-demand evaluations, such as the following\n\t\t\texample:

\n\t\t
    \n
  1. \n\t\t\t\t

    You have a custom rule that evaluates your IAM\n\t\t\t\t\tresources every 24 hours.

    \n\t\t\t
  2. \n
  3. \n\t\t\t\t

    You update your Lambda function to add additional\n\t\t\t\t\tconditions to your rule.

    \n\t\t\t
  4. \n
  5. \n\t\t\t\t

    Instead of waiting for the next periodic evaluation,\n\t\t\t\t\tyou call the StartConfigRulesEvaluation\n\t\t\t\t\tAPI.

    \n\t\t\t
  6. \n
  7. \n\t\t\t\t

    Config invokes your Lambda function and evaluates\n\t\t\t\t\tyour IAM resources.

    \n\t\t\t
  8. \n
  9. \n\t\t\t\t

    Your custom rule will still run periodic evaluations\n\t\t\t\t\tevery 24 hours.

    \n\t\t\t
  10. \n
" + "smithy.api#documentation": "

Runs an on-demand evaluation for the specified Config rules\n\t\t\tagainst the last known configuration state of the resources. Use\n\t\t\t\tStartConfigRulesEvaluation when you want to test\n\t\t\tthat a rule you updated is working as expected.\n\t\t\t\tStartConfigRulesEvaluation does not re-record the\n\t\t\tlatest configuration state for your resources. It re-runs an\n\t\t\tevaluation against the last known state of your resources.

\n

You can specify up to 25 Config rules per request.

\n

An existing StartConfigRulesEvaluation call for\n\t\t\tthe specified rules must complete before you can call the API again.\n\t\t\tIf you chose to have Config stream to an Amazon SNS topic, you\n\t\t\twill receive a ConfigRuleEvaluationStarted notification\n\t\t\twhen the evaluation starts.

\n \n

You don't need to call the\n\t\t\t\t\tStartConfigRulesEvaluation API to run an\n\t\t\t\tevaluation for a new rule. When you create a rule, Config\n\t\t\t\tevaluates your resources against the rule automatically.\n\t\t\t

\n
\n

The StartConfigRulesEvaluation API is useful if\n\t\t\tyou want to run on-demand evaluations, such as the following\n\t\t\texample:

\n
    \n
  1. \n

    You have a custom rule that evaluates your IAM\n\t\t\t\t\tresources every 24 hours.

    \n
  2. \n
  3. \n

    You update your Lambda function to add additional\n\t\t\t\t\tconditions to your rule.

    \n
  4. \n
  5. \n

    Instead of waiting for the next periodic evaluation,\n\t\t\t\t\tyou call the StartConfigRulesEvaluation\n\t\t\t\t\tAPI.

    \n
  6. \n
  7. \n

    Config invokes your Lambda function and evaluates\n\t\t\t\t\tyour IAM resources.

    \n
  8. \n
  9. \n

    Your custom rule will still run periodic evaluations\n\t\t\t\t\tevery 24 hours.

    \n
  10. \n
" } }, "com.amazonaws.configservice#StartConfigRulesEvaluationRequest": { @@ -13933,14 +15230,16 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#StartConfigRulesEvaluationResponse": { "type": "structure", "members": {}, "traits": { - "smithy.api#documentation": "

The output when you start the evaluation for the specified Config rule.

" + "smithy.api#documentation": "

The output when you start the evaluation for the specified Config rule.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#StartConfigurationRecorder": { @@ -13960,7 +15259,7 @@ } ], "traits": { - "smithy.api#documentation": "

Starts recording configurations of the Amazon Web Services resources you have\n\t\t\tselected to record in your Amazon Web Services account.

\n\t\t

You must have created at least one delivery channel to\n\t\t\tsuccessfully start the configuration recorder.

" + "smithy.api#documentation": "

Starts recording configurations of the Amazon Web Services resources you have\n\t\t\tselected to record in your Amazon Web Services account.

\n

You must have created at least one delivery channel to\n\t\t\tsuccessfully start the configuration recorder.

" } }, "com.amazonaws.configservice#StartConfigurationRecorderRequest": { @@ -13975,7 +15274,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the StartConfigurationRecorder\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the StartConfigurationRecorder\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#StartRemediationExecution": { @@ -13998,7 +15298,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs an on-demand remediation for the specified Config rules against the last known remediation configuration. It runs an execution against the current state of your resources. Remediation execution is asynchronous.

\n\t\t\t

You can specify up to 100 resource keys per request. An existing StartRemediationExecution call for the specified resource keys must complete before you can call the API again.

" + "smithy.api#documentation": "

Runs an on-demand remediation for the specified Config rules against the last known remediation configuration. It runs an execution against the current state of your resources. Remediation execution is asynchronous.

\n

You can specify up to 100 resource keys per request. An existing StartRemediationExecution call for the specified resource keys must complete before you can call the API again.

" } }, "com.amazonaws.configservice#StartRemediationExecutionRequest": { @@ -14018,6 +15318,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#StartRemediationExecutionResponse": { @@ -14035,6 +15338,9 @@ "smithy.api#documentation": "

For resources that have failed to start execution, the API returns a resource key object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#StartResourceEvaluation": { @@ -14054,7 +15360,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs an on-demand evaluation for the specified resource to determine whether the resource details will comply with configured Config rules.\n\t\t\tYou can also use it for evaluation purposes. Config recommends using an evaluation context. It runs an execution against the resource details with all\n\t\t\tof the Config rules in your account that match with the specified proactive mode and resource type.

\n\t\t\n\t\t \n

Ensure you have the cloudformation:DescribeType role setup to validate the resource type schema.\n\t\t

\n
" + "smithy.api#documentation": "

Runs an on-demand evaluation for the specified resource to determine whether the resource details will comply with configured Config rules.\n\t\t\tYou can also use it for evaluation purposes. Config recommends using an evaluation context. It runs an execution against the resource details with all\n\t\t\tof the Config rules in your account that match with the specified proactive mode and resource type.

\n \n

Ensure you have the cloudformation:DescribeType role setup to validate the resource type schema.

\n

You can find the\n\t\t\t\tResource type schema in \"Amazon Web Services public extensions\" within the CloudFormation registry or with the following CLI commmand:\n\t\t\taws cloudformation describe-type --type-name \"AWS::S3::Bucket\" --type RESOURCE.

\n

For more information, see Managing extensions through the CloudFormation registry\n\t\t\tand Amazon Web Services resource and property types reference in the CloudFormation User Guide.

\n
" } }, "com.amazonaws.configservice#StartResourceEvaluationRequest": { @@ -14076,7 +15382,7 @@ "EvaluationMode": { "target": "com.amazonaws.configservice#EvaluationMode", "traits": { - "smithy.api#documentation": "

The mode of an evaluation. The valid value for this API is Proactive.

", + "smithy.api#documentation": "

The mode of an evaluation. The valid values for this API are DETECTIVE and PROACTIVE.

", "smithy.api#required": {} } }, @@ -14090,9 +15396,12 @@ "ClientToken": { "target": "com.amazonaws.configservice#ClientToken", "traits": { - "smithy.api#documentation": "

A client token is a unique, case-sensitive string of up to 64 ASCII characters. \n\t\t\tTo make an idempotent API request using one of these actions, specify a client token in the request.

\n\t\t \n

Avoid reusing the same client token for other API requests. If you retry\n\t\t\t\ta request that completed successfully using the same client token and the same\n\t\t\t\tparameters, the retry succeeds without performing any further actions. If you retry\n\t\t\t\ta successful request using the same client token, but one or more of the parameters\n\t\t\t\tare different, other than the Region or Availability Zone, the retry fails with an\n\t\t\t\tIdempotentParameterMismatch error.

\n
" + "smithy.api#documentation": "

A client token is a unique, case-sensitive string of up to 64 ASCII characters. \n\t\t\tTo make an idempotent API request using one of these actions, specify a client token in the request.

\n \n

Avoid reusing the same client token for other API requests. If you retry\n\t\t\t\ta request that completed successfully using the same client token and the same\n\t\t\t\tparameters, the retry succeeds without performing any further actions. If you retry\n\t\t\t\ta successful request using the same client token, but one or more of the parameters\n\t\t\t\tare different, other than the Region or Availability Zone, the retry fails with an\n\t\t\t\tIdempotentParameterMismatch error.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#StartResourceEvaluationResponse": { @@ -14104,6 +15413,9 @@ "smithy.api#documentation": "

A\n\t\t\tunique ResourceEvaluationId that is associated with a single execution.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#StaticParameterValues": { @@ -14145,7 +15457,7 @@ "MemberAccountRuleStatus": { "target": "com.amazonaws.configservice#MemberAccountRuleStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status for Config rule in the member account.\n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in the member account. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t\t

\n\t\t

Config sets the state of the rule to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when Config rule has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when Config rule is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when Config rule creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when Config rule is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when Config rule has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when Config rule has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when Config rule is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
" + "smithy.api#documentation": "

Indicates deployment status for Config rule in the member account.\n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in the member account. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t\t

\n

Config sets the state of the rule to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when Config rule has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when Config rule is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when Config rule creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when Config rule is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when Config rule has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when Config rule has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when Config rule is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
" } } }, @@ -14182,7 +15494,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the StopConfigurationRecorder action.

" + "smithy.api#documentation": "

The input for the StopConfigurationRecorder action.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#StoredQuery": { @@ -14419,7 +15732,7 @@ } ], "traits": { - "smithy.api#documentation": "

Associates the specified tags to a resource with the specified resourceArn. If existing tags on a resource are not specified in the request parameters, they are not changed. \n\t\t\tWhen a resource is deleted, the tags associated with that resource are deleted as well.

" + "smithy.api#documentation": "

Associates the specified tags to a resource with the specified resourceArn. If existing tags on a resource are not specified in the request parameters, they are not changed.\n\t\t\tIf existing tags are specified, however, then their values will be updated. When a resource is deleted, the tags associated with that resource are deleted as well.

" } }, "com.amazonaws.configservice#TagResourceRequest": { @@ -14439,6 +15752,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#TagValue": { @@ -14503,12 +15819,12 @@ "DocumentVersion": { "target": "com.amazonaws.configservice#SSMDocumentVersion", "traits": { - "smithy.api#documentation": "

The version of the SSM document to use to create a conformance pack. By default, Config uses the latest version.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The version of the SSM document to use to create a conformance pack. By default, Config uses the latest version.

\n \n

This field is optional.

\n
" } } }, "traits": { - "smithy.api#documentation": "

This API allows you to create a conformance pack template with an Amazon Web Services Systems Manager document (SSM document). \n\t\t\tTo deploy a conformance pack using an SSM document, first create an SSM document with conformance pack content, and then provide the DocumentName in the PutConformancePack API. You can also provide the DocumentVersion.

\n\t\t\n\t\t

The TemplateSSMDocumentDetails object contains the name of the SSM document and the version of the SSM document.

" + "smithy.api#documentation": "

This API allows you to create a conformance pack template with an Amazon Web Services Systems Manager document (SSM document). \n\t\t\tTo deploy a conformance pack using an SSM document, first create an SSM document with conformance pack content, and then provide the DocumentName in the PutConformancePack API. You can also provide the DocumentVersion.

\n

The TemplateSSMDocumentDetails object contains the name of the SSM document and the version of the SSM document.

" } }, "com.amazonaws.configservice#TimeWindow": { @@ -14542,7 +15858,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of tags you can use. \n\t\t\tFor more information, see \n Service Limits\n in the Config Developer Guide.

", + "smithy.api#documentation": "

You have reached the limit of the number of tags you can use. \n\t\t\tFor more information, see \n Service Limits\n in the Config Developer Guide.

", "smithy.api#error": "client" } }, @@ -14589,6 +15905,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ValidationException": { @@ -14602,7 +15921,7 @@ } }, "traits": { - "smithy.api#documentation": "

The requested action is invalid.

\n\t\t

For PutStoredQuery, you will see this exception if there are missing required fields or if the input value fails the validation, or if you are trying to create more than 300 queries.

\n\t\t

For GetStoredQuery, ListStoredQuery, and DeleteStoredQuery you will see this exception if there are missing required fields or if the input value fails the validation.

", + "smithy.api#documentation": "

The requested action is not valid.

\n

For PutStoredQuery, you will see this exception if there are missing required fields or if the input value fails the validation, or if you are trying to create more than 300 queries.

\n

For GetStoredQuery, ListStoredQuery, and DeleteStoredQuery you will see this exception if there are missing required fields or if the input value fails the validation.

", "smithy.api#error": "client" } }, diff --git a/aws/sdk/aws-models/dynamodb.json b/aws/sdk/aws-models/dynamodb.json index c6a6550e5b..7c5a2d7678 100644 --- a/aws/sdk/aws-models/dynamodb.json +++ b/aws/sdk/aws-models/dynamodb.json @@ -833,7 +833,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The BatchGetItem operation returns the attributes of one or more items\n from one or more tables. You identify requested items by primary key.

\n

A single operation can retrieve up to 16 MB of data, which can contain as many as 100\n items. BatchGetItem returns a partial result if the response size limit is\n exceeded, the table's provisioned throughput is exceeded, or an internal processing\n failure occurs. If a partial result is returned, the operation returns a value for\n UnprocessedKeys. You can use this value to retry the operation starting\n with the next item to get.

\n \n

If you request more than 100 items, BatchGetItem returns a\n ValidationException with the message \"Too many items requested for\n the BatchGetItem call.\"

\n
\n

For example, if you ask to retrieve 100 items, but each individual item is 300 KB in\n size, the system returns 52 items (so as not to exceed the 16 MB limit). It also returns\n an appropriate UnprocessedKeys value so you can get the next page of\n results. If desired, your application can include its own logic to assemble the pages of\n results into one dataset.

\n

If none of the items can be processed due to insufficient\n provisioned throughput on all of the tables in the request, then\n BatchGetItem returns a\n ProvisionedThroughputExceededException. If at least\n one of the items is successfully processed, then\n BatchGetItem completes successfully, while returning the keys of the\n unread items in UnprocessedKeys.

\n \n

If DynamoDB returns any unprocessed items, you should retry the batch operation on\n those items. However, we strongly recommend that you use an exponential\n backoff algorithm. If you retry the batch operation immediately, the\n underlying read or write requests can still fail due to throttling on the individual\n tables. If you delay the batch operation using exponential backoff, the individual\n requests in the batch are much more likely to succeed.

\n

For more information, see Batch Operations and Error Handling in the Amazon DynamoDB\n Developer Guide.

\n
\n

By default, BatchGetItem performs eventually consistent reads on every\n table in the request. If you want strongly consistent reads instead, you can set\n ConsistentRead to true for any or all tables.

\n

In order to minimize response latency, BatchGetItem retrieves items in\n parallel.

\n

When designing your application, keep in mind that DynamoDB does not return items in\n any particular order. To help parse the response by item, include the primary key values\n for the items in your request in the ProjectionExpression parameter.

\n

If a requested item does not exist, it is not returned in the result. Requests for\n nonexistent items consume the minimum read capacity units according to the type of read.\n For more information, see Working with Tables in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The BatchGetItem operation returns the attributes of one or more items\n from one or more tables. You identify requested items by primary key.

\n

A single operation can retrieve up to 16 MB of data, which can contain as many as 100\n items. BatchGetItem returns a partial result if the response size limit is\n exceeded, the table's provisioned throughput is exceeded, more than 1MB per partition is requested,\n or an internal processing failure occurs. If a partial result is returned, the operation returns a value for\n UnprocessedKeys. You can use this value to retry the operation starting\n with the next item to get.

\n \n

If you request more than 100 items, BatchGetItem returns a\n ValidationException with the message \"Too many items requested for\n the BatchGetItem call.\"

\n
\n

For example, if you ask to retrieve 100 items, but each individual item is 300 KB in\n size, the system returns 52 items (so as not to exceed the 16 MB limit). It also returns\n an appropriate UnprocessedKeys value so you can get the next page of\n results. If desired, your application can include its own logic to assemble the pages of\n results into one dataset.

\n

If none of the items can be processed due to insufficient\n provisioned throughput on all of the tables in the request, then\n BatchGetItem returns a\n ProvisionedThroughputExceededException. If at least\n one of the items is successfully processed, then\n BatchGetItem completes successfully, while returning the keys of the\n unread items in UnprocessedKeys.

\n \n

If DynamoDB returns any unprocessed items, you should retry the batch operation on\n those items. However, we strongly recommend that you use an exponential\n backoff algorithm. If you retry the batch operation immediately, the\n underlying read or write requests can still fail due to throttling on the individual\n tables. If you delay the batch operation using exponential backoff, the individual\n requests in the batch are much more likely to succeed.

\n

For more information, see Batch Operations and Error Handling in the Amazon DynamoDB\n Developer Guide.

\n
\n

By default, BatchGetItem performs eventually consistent reads on every\n table in the request. If you want strongly consistent reads instead, you can set\n ConsistentRead to true for any or all tables.

\n

In order to minimize response latency, BatchGetItem may retrieve items in\n parallel.

\n

When designing your application, keep in mind that DynamoDB does not return items in\n any particular order. To help parse the response by item, include the primary key values\n for the items in your request in the ProjectionExpression parameter.

\n

If a requested item does not exist, it is not returned in the result. Requests for\n nonexistent items consume the minimum read capacity units according to the type of read.\n For more information, see Working with Tables in the Amazon DynamoDB Developer\n Guide.

" } }, "com.amazonaws.dynamodb#BatchGetItemInput": { @@ -3924,8 +3924,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3937,8 +3937,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3950,8 +3950,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3963,8 +3963,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3976,8 +3976,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3989,8 +3989,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4002,8 +4002,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4015,8 +4015,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4028,8 +4028,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4041,8 +4041,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4054,8 +4054,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4067,8 +4067,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4080,8 +4080,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4093,8 +4093,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4106,8 +4106,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4119,8 +4119,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4132,8 +4132,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4154,8 +4154,8 @@ }, "params": { "Region": "local", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4167,8 +4167,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4180,8 +4180,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4193,8 +4193,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4206,8 +4206,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4219,8 +4219,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4232,8 +4232,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4245,8 +4245,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4258,8 +4258,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4271,8 +4271,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4284,8 +4284,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4297,8 +4297,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4310,8 +4310,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4323,8 +4323,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4336,8 +4336,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4349,8 +4349,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4362,8 +4362,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4375,8 +4375,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4388,8 +4388,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4401,8 +4401,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4414,8 +4414,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4427,8 +4427,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4440,8 +4440,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4453,8 +4453,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4466,8 +4466,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4479,8 +4479,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4492,8 +4503,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4505,8 +4527,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4518,8 +4551,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4531,8 +4575,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4544,8 +4588,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4556,8 +4600,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4568,10 +4612,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -6682,7 +6732,7 @@ } }, "traits": { - "smithy.api#documentation": "

There is no limit to the number of daily on-demand backups that can be taken.

\n

For most purposes, up to 500 simultaneous table operations are allowed per account. These operations\n include CreateTable, UpdateTable,\n DeleteTable,UpdateTimeToLive,\n RestoreTableFromBackup, and RestoreTableToPointInTime.

\n

When you are creating a table with one or more secondary\n indexes, you can have up to 250 such requests running at a time. However, if the table or\n index specifications are complex, then DynamoDB might temporarily reduce the number\n of concurrent operations.

\n

When importing into DynamoDB, up to 50 simultaneous import table operations are allowed per account.

\n

There is a soft account quota of 2,500 tables.

", + "smithy.api#documentation": "

There is no limit to the number of daily on-demand backups that can be taken.

\n

For most purposes, up to 500 simultaneous table operations are allowed per account. These operations\n include CreateTable, UpdateTable,\n DeleteTable,UpdateTimeToLive,\n RestoreTableFromBackup, and RestoreTableToPointInTime.

\n

When you are creating a table with one or more secondary\n indexes, you can have up to 250 such requests running at a time. However, if the table or\n index specifications are complex, then DynamoDB might temporarily reduce the number\n of concurrent operations.

\n

When importing into DynamoDB, up to 50 simultaneous import table operations are allowed per account.

\n

There is a soft account quota of 2,500 tables.

\n

GetRecords was called with a value of more than 1000 for the limit request parameter.

\n

More than 2 processes are reading from the same streams shard at the same time. Exceeding\n this limit may result in request throttling.

", "smithy.api#error": "client" } }, @@ -7639,14 +7689,14 @@ "ReadCapacityUnits": { "target": "com.amazonaws.dynamodb#PositiveLongObject", "traits": { - "smithy.api#documentation": "

The maximum number of strongly consistent reads consumed per second before DynamoDB\n returns a ThrottlingException. For more information, see Specifying Read and Write Requirements in the Amazon DynamoDB\n Developer Guide.

\n

If read/write capacity mode is PAY_PER_REQUEST the value is set to\n 0.

", + "smithy.api#documentation": "

The maximum number of strongly consistent reads consumed per second before DynamoDB\n returns a ThrottlingException. For more information, see Specifying Read and Write Requirements in the Amazon DynamoDB\n Developer Guide.

\n

If read/write capacity mode is PAY_PER_REQUEST the value is set to\n 0.

", "smithy.api#required": {} } }, "WriteCapacityUnits": { "target": "com.amazonaws.dynamodb#PositiveLongObject", "traits": { - "smithy.api#documentation": "

The maximum number of writes consumed per second before DynamoDB returns a\n ThrottlingException. For more information, see Specifying Read and Write Requirements in the Amazon DynamoDB\n Developer Guide.

\n

If read/write capacity mode is PAY_PER_REQUEST the value is set to\n 0.

", + "smithy.api#documentation": "

The maximum number of writes consumed per second before DynamoDB returns a\n ThrottlingException. For more information, see Specifying Read and Write Requirements in the Amazon DynamoDB\n Developer Guide.

\n

If read/write capacity mode is PAY_PER_REQUEST the value is set to\n 0.

", "smithy.api#required": {} } } @@ -8863,7 +8913,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Creates a new table from an existing backup. Any number of users can execute up to 4\n concurrent restores (any type of restore) in a given account.

\n

You can call RestoreTableFromBackup at a maximum rate of 10 times per\n second.

\n

You must manually set up the following on the restored table:

\n
    \n
  • \n

    Auto scaling policies

    \n
  • \n
  • \n

    IAM policies

    \n
  • \n
  • \n

    Amazon CloudWatch metrics and alarms

    \n
  • \n
  • \n

    Tags

    \n
  • \n
  • \n

    Stream settings

    \n
  • \n
  • \n

    Time to Live (TTL) settings

    \n
  • \n
" + "smithy.api#documentation": "

Creates a new table from an existing backup. Any number of users can execute up to 50\n concurrent restores (any type of restore) in a given account.

\n

You can call RestoreTableFromBackup at a maximum rate of 10 times per\n second.

\n

You must manually set up the following on the restored table:

\n
    \n
  • \n

    Auto scaling policies

    \n
  • \n
  • \n

    IAM policies

    \n
  • \n
  • \n

    Amazon CloudWatch metrics and alarms

    \n
  • \n
  • \n

    Tags

    \n
  • \n
  • \n

    Stream settings

    \n
  • \n
  • \n

    Time to Live (TTL) settings

    \n
  • \n
" } }, "com.amazonaws.dynamodb#RestoreTableFromBackupInput": { diff --git a/aws/sdk/aws-models/ec2.json b/aws/sdk/aws-models/ec2.json index b159cff5a5..4d35d56d54 100644 --- a/aws/sdk/aws-models/ec2.json +++ b/aws/sdk/aws-models/ec2.json @@ -325,6 +325,9 @@ "smithy.api#xmlName": "addressTransfer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptReservedInstancesExchangeQuote": { @@ -385,7 +388,8 @@ } }, "traits": { - "smithy.api#documentation": "

The result of the exchange and whether it was successful.

" + "smithy.api#documentation": "

The result of the exchange and whether it was successful.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptTransitGatewayMulticastDomainAssociations": { @@ -445,6 +449,9 @@ "smithy.api#xmlName": "associations" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptTransitGatewayPeeringAttachment": { @@ -494,6 +501,9 @@ "smithy.api#xmlName": "transitGatewayPeeringAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptTransitGatewayVpcAttachment": { @@ -543,6 +553,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptVpcEndpointConnections": { @@ -601,6 +614,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptVpcPeeringConnection": { @@ -654,6 +670,9 @@ "smithy.api#xmlName": "vpcPeeringConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AccessScopeAnalysisFinding": { @@ -1060,7 +1079,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AdditionalDetailType", - "smithy.api#documentation": "

The information type.

", + "smithy.api#documentation": "

The additional detail code.

", "smithy.api#xmlName": "additionalDetailType" } }, @@ -1071,10 +1090,58 @@ "smithy.api#documentation": "

The path component.

", "smithy.api#xmlName": "component" } + }, + "VpcEndpointService": { + "target": "com.amazonaws.ec2#AnalysisComponent", + "traits": { + "aws.protocols#ec2QueryName": "VpcEndpointService", + "smithy.api#documentation": "

The VPC endpoint service.

", + "smithy.api#xmlName": "vpcEndpointService" + } + }, + "RuleOptions": { + "target": "com.amazonaws.ec2#RuleOptionList", + "traits": { + "aws.protocols#ec2QueryName": "RuleOptionSet", + "smithy.api#documentation": "

The rule options.

", + "smithy.api#xmlName": "ruleOptionSet" + } + }, + "RuleGroupTypePairs": { + "target": "com.amazonaws.ec2#RuleGroupTypePairList", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupTypePairSet", + "smithy.api#documentation": "

The rule group type.

", + "smithy.api#xmlName": "ruleGroupTypePairSet" + } + }, + "RuleGroupRuleOptionsPairs": { + "target": "com.amazonaws.ec2#RuleGroupRuleOptionsPairList", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupRuleOptionsPairSet", + "smithy.api#documentation": "

The rule options.

", + "smithy.api#xmlName": "ruleGroupRuleOptionsPairSet" + } + }, + "ServiceName": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "ServiceName", + "smithy.api#documentation": "

The name of the VPC endpoint service.

", + "smithy.api#xmlName": "serviceName" + } + }, + "LoadBalancers": { + "target": "com.amazonaws.ec2#AnalysisComponentList", + "traits": { + "aws.protocols#ec2QueryName": "LoadBalancerSet", + "smithy.api#documentation": "

The load balancers.

", + "smithy.api#xmlName": "loadBalancerSet" + } } }, "traits": { - "smithy.api#documentation": "

Describes an additional detail for a path analysis.

" + "smithy.api#documentation": "

Describes an additional detail for a path analysis. For more information, see Reachability Analyzer additional detail codes.

" } }, "com.amazonaws.ec2#AdditionalDetailList": { @@ -1109,7 +1176,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AllocationId", - "smithy.api#documentation": "

The ID representing the allocation of the address for use with EC2-VPC.

", + "smithy.api#documentation": "

The ID representing the allocation of the address.

", "smithy.api#xmlName": "allocationId" } }, @@ -1117,7 +1184,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AssociationId", - "smithy.api#documentation": "

The ID representing the association of the address with an instance in a VPC.

", + "smithy.api#documentation": "

The ID representing the association of the address with an instance.

", "smithy.api#xmlName": "associationId" } }, @@ -1125,7 +1192,7 @@ "target": "com.amazonaws.ec2#DomainType", "traits": { "aws.protocols#ec2QueryName": "Domain", - "smithy.api#documentation": "

Indicates whether this Elastic IP address is for use with instances\n\t\t\t\tin EC2-Classic (standard) or instances in a VPC (vpc).

", + "smithy.api#documentation": "

The network (vpc).

", "smithy.api#xmlName": "domain" } }, @@ -1437,6 +1504,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#Affinity": { @@ -1465,7 +1535,7 @@ "target": "com.amazonaws.ec2#AllocateAddressResult" }, "traits": { - "smithy.api#documentation": "

Allocates an Elastic IP address to your Amazon Web Services account. After you allocate the Elastic IP address you can associate \n it with an instance or network interface. After you release an Elastic IP address, it is released to the IP address \n pool and can be allocated to a different Amazon Web Services account.

\n

You can allocate an Elastic IP address from an address pool owned by Amazon Web Services or from an address pool created \n from a public IPv4 address range that you have brought to Amazon Web Services for use with your Amazon Web Services resources using bring your own \n IP addresses (BYOIP). For more information, see Bring Your Own IP Addresses (BYOIP) in the Amazon Elastic Compute Cloud User Guide.

\n

[EC2-VPC] If you release an Elastic IP address, you might be able to recover it. You cannot recover an \n Elastic IP address that you released after it is allocated to another Amazon Web Services account. You cannot recover an Elastic IP\n address for EC2-Classic. To attempt to recover an Elastic IP address that you released, specify it in this operation.

\n

An Elastic IP address is for use either in the EC2-Classic platform or in a VPC. By default, you can allocate\n 5 Elastic IP addresses for EC2-Classic per Region and 5 Elastic IP addresses for EC2-VPC per Region.

\n

For more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

\n

You can allocate a carrier IP address which is a public IP address from a telecommunication carrier, to a network interface which resides in a subnet in a Wavelength Zone (for example an EC2 instance).

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Allocates an Elastic IP address to your Amazon Web Services account. After you allocate the Elastic IP address you can associate \n it with an instance or network interface. After you release an Elastic IP address, it is released to the IP address \n pool and can be allocated to a different Amazon Web Services account.

\n

You can allocate an Elastic IP address from an address pool owned by Amazon Web Services or from an address pool created \n from a public IPv4 address range that you have brought to Amazon Web Services for use with your Amazon Web Services resources using bring your own \n IP addresses (BYOIP). For more information, see Bring Your Own IP Addresses (BYOIP) in the Amazon Elastic Compute Cloud User Guide.

\n

If you release an Elastic IP address, you might be able to recover it. You cannot recover\n an Elastic IP address that you released after it is allocated to another Amazon Web Services account. To attempt to recover an Elastic IP address that you released, specify\n it in this operation.

\n

For more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

\n

You can allocate a carrier IP address which is a public IP address from a telecommunication carrier, \n to a network interface which resides in a subnet in a Wavelength Zone (for example an EC2 instance).

" } }, "com.amazonaws.ec2#AllocateAddressRequest": { @@ -1474,13 +1544,13 @@ "Domain": { "target": "com.amazonaws.ec2#DomainType", "traits": { - "smithy.api#documentation": "

Indicates whether the Elastic IP address is for use with instances in a VPC or instances in EC2-Classic.

\n

Default: If the Region supports EC2-Classic, the default is standard. Otherwise, the default\n is vpc.

" + "smithy.api#documentation": "

The network (vpc).

" } }, "Address": { "target": "com.amazonaws.ec2#PublicIpAddress", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The Elastic IP address to recover or an IPv4 address from an address pool.

" + "smithy.api#documentation": "

The Elastic IP address to recover or an IPv4 address from an address pool.

" } }, "PublicIpv4Pool": { @@ -1538,7 +1608,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AllocationId", - "smithy.api#documentation": "

[EC2-VPC] The ID that Amazon Web Services assigns to represent the allocation of the Elastic IP address for use with instances in a VPC.

", + "smithy.api#documentation": "

The ID that represents the allocation of the Elastic IP address.

", "smithy.api#xmlName": "allocationId" } }, @@ -1562,7 +1632,7 @@ "target": "com.amazonaws.ec2#DomainType", "traits": { "aws.protocols#ec2QueryName": "Domain", - "smithy.api#documentation": "

Indicates whether the Elastic IP address is for use with instances in a VPC (vpc) or\n\t\t\t\tinstances in EC2-Classic (standard).

", + "smithy.api#documentation": "

The network (vpc).

", "smithy.api#xmlName": "domain" } }, @@ -1586,10 +1656,13 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "CarrierIp", - "smithy.api#documentation": "

The carrier IP address. This option is only available for network interfaces which reside\n in a subnet in a Wavelength Zone (for example an EC2 instance).

", + "smithy.api#documentation": "

The carrier IP address. This option is only available for network interfaces that reside\n in a subnet in a Wavelength Zone.

", "smithy.api#xmlName": "carrierIp" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AllocateHosts": { @@ -1701,7 +1774,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of AllocateHosts.

" + "smithy.api#documentation": "

Contains the output of AllocateHosts.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#AllocateIpamPoolCidr": { @@ -1713,7 +1787,7 @@ "target": "com.amazonaws.ec2#AllocateIpamPoolCidrResult" }, "traits": { - "smithy.api#documentation": "

Allocate a CIDR from an IPAM pool. In IPAM, an allocation is a CIDR assignment from an IPAM pool to another IPAM pool or to a resource. For more information, see Allocate CIDRs in the Amazon VPC IPAM User Guide.\n

" + "smithy.api#documentation": "

Allocate a CIDR from an IPAM pool. The Region you use should be the IPAM pool locale. The locale is the Amazon Web Services Region where this IPAM pool is available for allocations.

\n

In IPAM, an allocation is a CIDR assignment from an IPAM pool to another IPAM pool or to a resource. For more information, see Allocate CIDRs in the Amazon VPC IPAM User Guide.

\n \n

This action creates an allocation with strong consistency. The returned CIDR will not overlap with any other allocations from the same pool.

\n
" } }, "com.amazonaws.ec2#AllocateIpamPoolCidrRequest": { @@ -1793,6 +1867,9 @@ "smithy.api#xmlName": "ipamPoolAllocation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AllocationId": { @@ -2249,6 +2326,9 @@ { "target": "com.amazonaws.ec2#CreateImage" }, + { + "target": "com.amazonaws.ec2#CreateInstanceConnectEndpoint" + }, { "target": "com.amazonaws.ec2#CreateInstanceEventWindow" }, @@ -2477,6 +2557,9 @@ { "target": "com.amazonaws.ec2#DeleteFpgaImage" }, + { + "target": "com.amazonaws.ec2#DeleteInstanceConnectEndpoint" + }, { "target": "com.amazonaws.ec2#DeleteInstanceEventWindow" }, @@ -2819,6 +2902,9 @@ { "target": "com.amazonaws.ec2#DescribeInstanceAttribute" }, + { + "target": "com.amazonaws.ec2#DescribeInstanceConnectEndpoints" + }, { "target": "com.amazonaws.ec2#DescribeInstanceCreditSpecifications" }, @@ -3392,6 +3478,9 @@ { "target": "com.amazonaws.ec2#GetVpnConnectionDeviceTypes" }, + { + "target": "com.amazonaws.ec2#GetVpnTunnelReplacementStatus" + }, { "target": "com.amazonaws.ec2#ImportClientVpnClientCertificateRevocationList" }, @@ -3692,6 +3781,9 @@ { "target": "com.amazonaws.ec2#ReplaceTransitGatewayRoute" }, + { + "target": "com.amazonaws.ec2#ReplaceVpnTunnel" + }, { "target": "com.amazonaws.ec2#ReportInstanceStatus" }, @@ -4175,44 +4267,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-east-1" - ] - } - ], - "endpoint": { - "url": "https://ec2.us-gov-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-west-1" - ] - } - ], - "endpoint": { - "url": "https://ec2.us-gov-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -4248,8 +4302,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4261,8 +4315,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4274,8 +4328,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4287,8 +4341,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4300,8 +4354,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4313,8 +4367,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4326,8 +4380,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4339,8 +4393,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4352,8 +4406,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4365,8 +4419,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4378,8 +4432,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4391,8 +4445,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4404,8 +4458,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4417,8 +4471,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4430,8 +4484,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4443,8 +4497,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4456,8 +4510,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4469,8 +4523,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4482,8 +4536,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4495,8 +4549,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4508,8 +4562,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4521,8 +4575,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4534,8 +4588,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4547,8 +4601,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4560,8 +4614,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4573,8 +4627,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4586,8 +4640,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4599,8 +4653,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4612,8 +4666,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4625,8 +4679,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4638,8 +4692,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4651,8 +4705,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4664,8 +4718,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4677,8 +4731,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4690,8 +4744,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4703,8 +4757,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4716,8 +4770,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4729,8 +4783,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4742,8 +4796,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4755,8 +4809,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4768,8 +4822,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4781,8 +4835,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4794,8 +4848,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4807,8 +4861,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4820,8 +4874,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4833,8 +4887,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4846,8 +4911,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4859,8 +4935,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4872,8 +4959,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4885,8 +4983,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4898,8 +4996,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4910,8 +5008,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4922,16 +5020,39 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" } } }, + "com.amazonaws.ec2#AmdSevSnpSpecification": { + "type": "enum", + "members": { + "enabled": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "enabled" + } + }, + "disabled": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "disabled" + } + } + } + }, "com.amazonaws.ec2#AnalysisAclRule": { "type": "structure", "members": { @@ -5241,6 +5362,30 @@ "smithy.api#documentation": "

The state. The following are the possible values:

\n
    \n
  • \n

    active

    \n
  • \n
  • \n

    blackhole

    \n
  • \n
", "smithy.api#xmlName": "state" } + }, + "CarrierGatewayId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "CarrierGatewayId", + "smithy.api#documentation": "

The ID of a carrier gateway.

", + "smithy.api#xmlName": "carrierGatewayId" + } + }, + "CoreNetworkArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "CoreNetworkArn", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of a core network.

", + "smithy.api#xmlName": "coreNetworkArn" + } + }, + "LocalGatewayId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "LocalGatewayId", + "smithy.api#documentation": "

The ID of a local gateway.

", + "smithy.api#xmlName": "localGatewayId" + } } }, "traits": { @@ -5407,6 +5552,9 @@ "smithy.api#xmlName": "securityGroupIds" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ArchitectureType": { @@ -5602,6 +5750,9 @@ "smithy.api#xmlName": "networkInterfaceId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssignPrivateIpAddresses": { @@ -5705,6 +5856,9 @@ "smithy.api#xmlName": "assignedIpv4PrefixSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssignPrivateNatGatewayAddress": { @@ -5777,6 +5931,9 @@ "smithy.api#xmlName": "natGatewayAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssignedPrivateIpAddress": { @@ -5813,7 +5970,7 @@ "target": "com.amazonaws.ec2#AssociateAddressResult" }, "traits": { - "smithy.api#documentation": "

Associates an Elastic IP address, or carrier IP address (for instances that are in\n subnets in Wavelength Zones) with an instance or a network interface. Before you can use an\n Elastic IP address, you must allocate it to your account.

\n

An Elastic IP address is for use in either the EC2-Classic platform or in a VPC.\n\t\t\tFor more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

\n

[EC2-Classic, VPC in an EC2-VPC-only account] If the Elastic IP address is already\n associated with a different instance, it is disassociated from that instance and associated\n with the specified instance. If you associate an Elastic IP address with an instance that has\n an existing Elastic IP address, the existing address is disassociated from the instance, but\n remains allocated to your account.

\n

[VPC in an EC2-Classic account] If you don't specify a private IP address, the Elastic\n IP address is associated with the primary IP address. If the Elastic IP address is already\n associated with a different instance or a network interface, you get an error unless you allow\n reassociation. You cannot associate an Elastic IP address with an instance or network\n interface that has an existing Elastic IP address.

\n

[Subnets in Wavelength Zones] You can associate an IP address from the telecommunication\n carrier to the instance or network interface.

\n

You cannot associate an Elastic IP address with an interface in a different network border group.

\n \n

This is an idempotent operation. If you perform the operation more than once, Amazon EC2\n doesn't return an error, and you may be charged for each time the Elastic IP address is\n remapped to the same instance. For more information, see the Elastic IP\n Addresses section of Amazon EC2\n Pricing.

\n
\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Associates an Elastic IP address, or carrier IP address (for instances that are in\n subnets in Wavelength Zones) with an instance or a network interface. Before you can use an\n Elastic IP address, you must allocate it to your account.

\n

If the Elastic IP address is already\n associated with a different instance, it is disassociated from that instance and associated\n with the specified instance. If you associate an Elastic IP address with an instance that has\n an existing Elastic IP address, the existing address is disassociated from the instance, but\n remains allocated to your account.

\n

[Subnets in Wavelength Zones] You can associate an IP address from the telecommunication\n carrier to the instance or network interface.

\n

You cannot associate an Elastic IP address with an interface in a different network border group.

\n \n

This is an idempotent operation. If you perform the operation more than once, Amazon EC2\n doesn't return an error, and you may be charged for each time the Elastic IP address is\n remapped to the same instance. For more information, see the Elastic IP\n Addresses section of Amazon EC2\n Pricing.

\n
" } }, "com.amazonaws.ec2#AssociateAddressRequest": { @@ -5822,19 +5979,19 @@ "AllocationId": { "target": "com.amazonaws.ec2#AllocationId", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The allocation ID. This is required for EC2-VPC.

" + "smithy.api#documentation": "

The allocation ID. This is required.

" } }, "InstanceId": { "target": "com.amazonaws.ec2#InstanceId", "traits": { - "smithy.api#documentation": "

The ID of the instance. The instance must have exactly one attached network interface.\n For EC2-VPC, you can specify either the instance ID or the network interface ID, but not both.\n For EC2-Classic, you must specify an instance ID and the instance must be in the running\n state.

" + "smithy.api#documentation": "

The ID of the instance. The instance must have exactly one attached network interface.\n You can specify either the instance ID or the network interface ID, but not both.

" } }, "PublicIp": { "target": "com.amazonaws.ec2#EipAllocationPublicIp", "traits": { - "smithy.api#documentation": "

[EC2-Classic] The Elastic IP address to associate with the instance. This is required for\n EC2-Classic.

" + "smithy.api#documentation": "

Deprecated.

" } }, "AllowReassociation": { @@ -5843,7 +6000,7 @@ "aws.protocols#ec2QueryName": "AllowReassociation", "smithy.api#clientOptional": {}, "smithy.api#default": false, - "smithy.api#documentation": "

[EC2-VPC] For a VPC in an EC2-Classic account, specify true to allow an Elastic IP address that is already associated with an instance or network interface to be reassociated with the specified instance or network interface. Otherwise, the operation fails. In a VPC in an EC2-VPC-only account, reassociation is automatic, therefore you can specify false to ensure the operation fails if the Elastic IP address is already associated with another resource.

", + "smithy.api#documentation": "

Reassociation is automatic, but you can specify false to ensure the operation fails if the Elastic IP address is already associated with another resource.

", "smithy.api#xmlName": "allowReassociation" } }, @@ -5861,7 +6018,7 @@ "target": "com.amazonaws.ec2#NetworkInterfaceId", "traits": { "aws.protocols#ec2QueryName": "NetworkInterfaceId", - "smithy.api#documentation": "

[EC2-VPC] The ID of the network interface. If the instance has more than one network interface, you must specify a network interface ID.

\n

For EC2-VPC, you can specify either the instance ID or the network interface ID, but not both.

", + "smithy.api#documentation": "

The ID of the network interface. If the instance has more than one network interface, you must specify a network interface ID.

\n

You can specify either the instance ID or the network interface ID, but not both.

", "smithy.api#xmlName": "networkInterfaceId" } }, @@ -5869,7 +6026,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PrivateIpAddress", - "smithy.api#documentation": "

[EC2-VPC] The primary or secondary private IP address to associate with the Elastic IP address. If no private IP address is specified, the Elastic IP address is associated with the primary private IP address.

", + "smithy.api#documentation": "

The primary or secondary private IP address to associate with the Elastic IP address. If no private IP address is specified, the Elastic IP address is associated with the primary private IP address.

", "smithy.api#xmlName": "privateIpAddress" } } @@ -5885,10 +6042,13 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AssociationId", - "smithy.api#documentation": "

[EC2-VPC] The ID that represents the association of the Elastic IP address with an instance.

", + "smithy.api#documentation": "

The ID that represents the association of the Elastic IP address with an instance.

", "smithy.api#xmlName": "associationId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateClientVpnTargetNetwork": { @@ -5961,6 +6121,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateDhcpOptions": { @@ -6080,6 +6243,9 @@ "smithy.api#xmlName": "encryptionKmsKeyId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateIamInstanceProfile": { @@ -6129,6 +6295,9 @@ "smithy.api#xmlName": "iamInstanceProfileAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateInstanceEventWindow": { @@ -6186,6 +6355,9 @@ "smithy.api#xmlName": "instanceEventWindow" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateIpamResourceDiscovery": { @@ -6257,6 +6429,9 @@ "smithy.api#xmlName": "ipamResourceDiscoveryAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateNatGatewayAddress": { @@ -6330,6 +6505,9 @@ "smithy.api#xmlName": "natGatewayAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateRouteTable": { @@ -6405,6 +6583,9 @@ "smithy.api#xmlName": "associationState" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateSubnetCidrBlock": { @@ -6466,6 +6647,9 @@ "smithy.api#xmlName": "subnetId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateTransitGatewayMulticastDomain": { @@ -6531,6 +6715,9 @@ "smithy.api#xmlName": "associations" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateTransitGatewayPolicyTable": { @@ -6588,6 +6775,9 @@ "smithy.api#xmlName": "association" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateTransitGatewayRouteTable": { @@ -6645,6 +6835,9 @@ "smithy.api#xmlName": "association" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateTrunkInterface": { @@ -6733,6 +6926,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateVpcCidrBlock": { @@ -6850,6 +7046,9 @@ "smithy.api#xmlName": "vpcId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociatedNetworkType": { @@ -7136,6 +7335,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AttachInternetGateway": { @@ -7287,7 +7489,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of AttachNetworkInterface.

" + "smithy.api#documentation": "

Contains the output of AttachNetworkInterface.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#AttachVerifiedAccessTrustProvider": { @@ -7299,7 +7502,7 @@ "target": "com.amazonaws.ec2#AttachVerifiedAccessTrustProviderResult" }, "traits": { - "smithy.api#documentation": "

A trust provider is a third-party entity that creates, maintains, and manages identity\n information for users and devices. One or more trust providers can be attached to an Amazon Web Services Verified Access\n instance.

" + "smithy.api#documentation": "

Attaches the specified Amazon Web Services Verified Access trust provider to the specified Amazon Web Services Verified Access instance.

" } }, "com.amazonaws.ec2#AttachVerifiedAccessTrustProviderRequest": { @@ -7309,7 +7512,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, @@ -7317,7 +7520,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#required": {} } }, @@ -7348,7 +7551,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } }, @@ -7356,10 +7559,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AttachVolume": { @@ -7476,7 +7682,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of AttachVpnGateway.

" + "smithy.api#documentation": "

Contains the output of AttachVpnGateway.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#AttachmentEnaSrdSpecification": { @@ -7735,6 +7942,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AuthorizeSecurityGroupEgress": { @@ -7865,6 +8075,9 @@ "smithy.api#xmlName": "securityGroupRuleSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AuthorizeSecurityGroupIngress": { @@ -7983,6 +8196,9 @@ "smithy.api#xmlName": "securityGroupRuleSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AutoAcceptSharedAssociationsValue": { @@ -8169,6 +8385,9 @@ } } }, + "com.amazonaws.ec2#AvailabilityZoneName": { + "type": "string" + }, "com.amazonaws.ec2#AvailabilityZoneOptInStatus": { "type": "enum", "members": { @@ -8628,7 +8847,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of BundleInstance.

" + "smithy.api#documentation": "

Contains the output of BundleInstance.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#BundleTask": { @@ -8992,7 +9212,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CancelBundleTask.

" + "smithy.api#documentation": "

Contains the output of CancelBundleTask.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelCapacityReservation": { @@ -9093,6 +9314,9 @@ "smithy.api#xmlName": "failedFleetCancellationSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelCapacityReservationRequest": { @@ -9132,6 +9356,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelConversionRequest": { @@ -9261,6 +9488,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelImportTask": { @@ -9330,6 +9560,9 @@ "smithy.api#xmlName": "state" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelReservedInstancesListing": { @@ -9376,7 +9609,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CancelReservedInstancesListing.

" + "smithy.api#documentation": "

Contains the output of CancelReservedInstancesListing.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelSpotFleetRequests": { @@ -9618,7 +9852,7 @@ "target": "com.amazonaws.ec2#SpotInstanceRequestIdList", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

One or more Spot Instance request IDs.

", + "smithy.api#documentation": "

The IDs of the Spot Instance requests.

", "smithy.api#required": {}, "smithy.api#xmlName": "SpotInstanceRequestId" } @@ -9636,13 +9870,14 @@ "target": "com.amazonaws.ec2#CancelledSpotInstanceRequestList", "traits": { "aws.protocols#ec2QueryName": "SpotInstanceRequestSet", - "smithy.api#documentation": "

One or more Spot Instance requests.

", + "smithy.api#documentation": "

The Spot Instance requests.

", "smithy.api#xmlName": "spotInstanceRequestSet" } } }, "traits": { - "smithy.api#documentation": "

Contains the output of CancelSpotInstanceRequests.

" + "smithy.api#documentation": "

Contains the output of CancelSpotInstanceRequests.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelledSpotInstanceRequest": { @@ -10990,6 +11225,12 @@ "smithy.api#documentation": "

Current state of options for customizable text banner that will be displayed on\n\t\t\tAmazon Web Services provided clients when a VPN session is established.

" } }, + "com.amazonaws.ec2#ClientSecretType": { + "type": "string", + "traits": { + "smithy.api#sensitive": {} + } + }, "com.amazonaws.ec2#ClientVpnAssociationId": { "type": "string" }, @@ -12084,6 +12325,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ConnectionLogOptions": { @@ -12461,6 +12705,9 @@ "smithy.api#xmlName": "fpgaImageId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CopyImage": { @@ -12575,7 +12822,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CopyImage.

" + "smithy.api#documentation": "

Contains the output of CopyImage.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CopySnapshot": { @@ -12702,6 +12950,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CopyTagsFromSource": { @@ -12784,6 +13035,14 @@ "smithy.api#documentation": "

The number of threads per CPU core.

", "smithy.api#xmlName": "threadsPerCore" } + }, + "AmdSevSnp": { + "target": "com.amazonaws.ec2#AmdSevSnpSpecification", + "traits": { + "aws.protocols#ec2QueryName": "AmdSevSnp", + "smithy.api#documentation": "

Indicates whether the instance is enabled for AMD SEV-SNP.

", + "smithy.api#xmlName": "amdSevSnp" + } } }, "traits": { @@ -12808,6 +13067,12 @@ "smithy.api#default": 0, "smithy.api#documentation": "

The number of threads per CPU core. To disable multithreading for the instance,\n specify a value of 1. Otherwise, specify the default value of\n 2.

" } + }, + "AmdSevSnp": { + "target": "com.amazonaws.ec2#AmdSevSnpSpecification", + "traits": { + "smithy.api#documentation": "

Indicates whether to enable the instance for AMD SEV-SNP. AMD SEV-SNP is supported \n with M6a, R6a, and C6a instance types only.

" + } } }, "traits": { @@ -13005,6 +13270,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCapacityReservationRequest": { @@ -13135,6 +13403,9 @@ "smithy.api#xmlName": "capacityReservation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCarrierGateway": { @@ -13198,6 +13469,9 @@ "smithy.api#xmlName": "carrierGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateClientVpnEndpoint": { @@ -13375,6 +13649,9 @@ "smithy.api#xmlName": "dnsName" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateClientVpnRoute": { @@ -13453,6 +13730,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCoipCidr": { @@ -13510,6 +13790,9 @@ "smithy.api#xmlName": "coipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCoipPool": { @@ -13566,6 +13849,9 @@ "smithy.api#xmlName": "coipPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCustomerGateway": { @@ -13659,7 +13945,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateCustomerGateway.

" + "smithy.api#documentation": "

Contains the output of CreateCustomerGateway.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateDefaultSubnet": { @@ -13717,6 +14004,9 @@ "smithy.api#xmlName": "subnet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateDefaultVpc": { @@ -13758,6 +14048,9 @@ "smithy.api#xmlName": "vpc" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateDhcpOptions": { @@ -13818,6 +14111,9 @@ "smithy.api#xmlName": "dhcpOptions" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateEgressOnlyInternetGateway": { @@ -13888,6 +14184,9 @@ "smithy.api#xmlName": "egressOnlyInternetGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateFleet": { @@ -14138,6 +14437,9 @@ "smithy.api#xmlName": "fleetInstanceSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateFlowLogs": { @@ -14281,6 +14583,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateFpgaImage": { @@ -14369,6 +14674,9 @@ "smithy.api#xmlName": "fpgaImageGlobalId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateImage": { @@ -14465,6 +14773,98 @@ "smithy.api#xmlName": "imageId" } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.ec2#CreateInstanceConnectEndpoint": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#CreateInstanceConnectEndpointRequest" + }, + "output": { + "target": "com.amazonaws.ec2#CreateInstanceConnectEndpointResult" + }, + "traits": { + "smithy.api#documentation": "

Creates an EC2 Instance Connect Endpoint.

\n

An EC2 Instance Connect Endpoint allows you to connect to a resource, without\n requiring the resource to have a public IPv4 address. For more information, see Connect to your resources without requiring a public IPv4 address using EC2\n Instance Connect Endpoint in the Amazon EC2 User\n Guide.

" + } + }, + "com.amazonaws.ec2#CreateInstanceConnectEndpointRequest": { + "type": "structure", + "members": { + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, \n and provides an error response. If you have the required permissions, the error response is DryRunOperation. \n Otherwise, it is UnauthorizedOperation.

" + } + }, + "SubnetId": { + "target": "com.amazonaws.ec2#SubnetId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The ID of the subnet in which to create the EC2 Instance Connect Endpoint.

", + "smithy.api#required": {} + } + }, + "SecurityGroupIds": { + "target": "com.amazonaws.ec2#SecurityGroupIdStringListRequest", + "traits": { + "smithy.api#documentation": "

One or more security groups to associate with the endpoint. If you don't specify a security group, \n the default security group for your VPC will be associated with the endpoint.

", + "smithy.api#xmlName": "SecurityGroupId" + } + }, + "PreserveClientIp": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Indicates whether your client's IP address is preserved as the source. The value is true or false.

\n
    \n
  • \n

    If true, your client's IP address is used when you connect to a resource.

    \n
  • \n
  • \n

    If false, the elastic network interface IP address is used when you connect to a resource.

    \n
  • \n
\n

Default: true\n

" + } + }, + "ClientToken": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

Unique, case-sensitive identifier that you provide to ensure the idempotency of the request.

", + "smithy.api#idempotencyToken": {} + } + }, + "TagSpecifications": { + "target": "com.amazonaws.ec2#TagSpecificationList", + "traits": { + "smithy.api#documentation": "

The tags to apply to the EC2 Instance Connect Endpoint during creation.

", + "smithy.api#xmlName": "TagSpecification" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#CreateInstanceConnectEndpointResult": { + "type": "structure", + "members": { + "InstanceConnectEndpoint": { + "target": "com.amazonaws.ec2#Ec2InstanceConnectEndpoint", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpoint", + "smithy.api#documentation": "

Information about the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "instanceConnectEndpoint" + } + }, + "ClientToken": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "ClientToken", + "smithy.api#documentation": "

Unique, case-sensitive idempotency token provided by the client in the the request.

", + "smithy.api#xmlName": "clientToken" + } + } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateInstanceEventWindow": { @@ -14532,6 +14932,9 @@ "smithy.api#xmlName": "instanceEventWindow" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateInstanceExportTask": { @@ -14610,6 +15013,9 @@ "smithy.api#xmlName": "exportTask" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateInternetGateway": { @@ -14660,6 +15066,9 @@ "smithy.api#xmlName": "internetGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateIpam": { @@ -14820,6 +15229,9 @@ "smithy.api#xmlName": "ipamPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateIpamRequest": { @@ -14931,6 +15343,9 @@ "smithy.api#xmlName": "ipamResourceDiscovery" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateIpamResult": { @@ -14944,6 +15359,9 @@ "smithy.api#xmlName": "ipam" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateIpamScope": { @@ -15013,6 +15431,9 @@ "smithy.api#xmlName": "ipamScope" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateKeyPair": { @@ -15154,6 +15575,9 @@ "smithy.api#xmlName": "warning" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLaunchTemplateVersion": { @@ -15249,6 +15673,9 @@ "smithy.api#xmlName": "warning" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLocalGatewayRoute": { @@ -15322,6 +15749,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLocalGatewayRouteTable": { @@ -15384,6 +15814,9 @@ "smithy.api#xmlName": "localGatewayRouteTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLocalGatewayRouteTableVirtualInterfaceGroupAssociation": { @@ -15448,6 +15881,9 @@ "smithy.api#xmlName": "localGatewayRouteTableVirtualInterfaceGroupAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLocalGatewayRouteTableVpcAssociation": { @@ -15512,6 +15948,9 @@ "smithy.api#xmlName": "localGatewayRouteTableVpcAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateManagedPrefixList": { @@ -15599,6 +16038,9 @@ "smithy.api#xmlName": "prefixList" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNatGateway": { @@ -15710,6 +16152,9 @@ "smithy.api#xmlName": "natGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkAcl": { @@ -15883,6 +16328,9 @@ "smithy.api#xmlName": "networkAcl" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkInsightsAccessScope": { @@ -15962,6 +16410,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeContent" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkInsightsPath": { @@ -15973,7 +16424,7 @@ "target": "com.amazonaws.ec2#CreateNetworkInsightsPathResult" }, "traits": { - "smithy.api#documentation": "

Creates a path to analyze for reachability.

\n

Reachability Analyzer enables you to analyze and debug network reachability between\n two resources in your virtual private cloud (VPC). For more information, see \n What is Reachability Analyzer.

" + "smithy.api#documentation": "

Creates a path to analyze for reachability.

\n

Reachability Analyzer enables you to analyze and debug network reachability between\n two resources in your virtual private cloud (VPC). For more information, see the \n Reachability Analyzer Guide.

" } }, "com.amazonaws.ec2#CreateNetworkInsightsPathRequest": { @@ -15982,29 +16433,27 @@ "SourceIp": { "target": "com.amazonaws.ec2#IpAddress", "traits": { - "smithy.api#documentation": "

The IP address of the Amazon Web Services resource that is the source of the path.

" + "smithy.api#documentation": "

The IP address of the source.

" } }, "DestinationIp": { "target": "com.amazonaws.ec2#IpAddress", "traits": { - "smithy.api#documentation": "

The IP address of the Amazon Web Services resource that is the destination of the path.

" + "smithy.api#documentation": "

The IP address of the destination.

" } }, "Source": { "target": "com.amazonaws.ec2#NetworkInsightsResourceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The Amazon Web Services resource that is the source of the path.

", + "smithy.api#documentation": "

The ID or ARN of the source. If the resource is in another account, you must specify an ARN.

", "smithy.api#required": {} } }, "Destination": { "target": "com.amazonaws.ec2#NetworkInsightsResourceId", "traits": { - "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The Amazon Web Services resource that is the destination of the path.

", - "smithy.api#required": {} + "smithy.api#documentation": "

The ID or ARN of the destination. If the resource is in another account, you must specify an ARN.

" } }, "Protocol": { @@ -16046,6 +16495,18 @@ "smithy.api#idempotencyToken": {}, "smithy.api#required": {} } + }, + "FilterAtSource": { + "target": "com.amazonaws.ec2#PathRequestFilter", + "traits": { + "smithy.api#documentation": "

Scopes the analysis to network paths that match specific filters at the source. If you specify\n this parameter, you can't specify the parameters for the source IP address or the destination port.

" + } + }, + "FilterAtDestination": { + "target": "com.amazonaws.ec2#PathRequestFilter", + "traits": { + "smithy.api#documentation": "

Scopes the analysis to network paths that match specific filters at the destination. If you specify\n this parameter, you can't specify the parameter for the destination IP address.

" + } } }, "traits": { @@ -16063,6 +16524,9 @@ "smithy.api#xmlName": "networkInsightsPath" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkInterface": { @@ -16147,7 +16611,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateNetworkInterfacePermission.

" + "smithy.api#documentation": "

Contains the output of CreateNetworkInterfacePermission.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkInterfaceRequest": { @@ -16255,7 +16720,7 @@ "InterfaceType": { "target": "com.amazonaws.ec2#NetworkInterfaceCreationType", "traits": { - "smithy.api#documentation": "

The type of network interface. The default is interface.

\n

The only supported values are efa and trunk.

" + "smithy.api#documentation": "

The type of network interface. The default is interface.

\n

The only supported values are interface, efa, and trunk.

" } }, "SubnetId": { @@ -16306,6 +16771,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreatePlacementGroup": { @@ -16386,6 +16854,9 @@ "smithy.api#xmlName": "placementGroup" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreatePublicIpv4Pool": { @@ -16434,6 +16905,9 @@ "smithy.api#xmlName": "poolId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateReplaceRootVolumeTask": { @@ -16517,6 +16991,9 @@ "smithy.api#xmlName": "replaceRootVolumeTask" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateReservedInstancesListing": { @@ -16594,7 +17071,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateReservedInstancesListing.

" + "smithy.api#documentation": "

Contains the output of CreateReservedInstancesListing.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateRestoreImageTask": { @@ -16665,6 +17143,9 @@ "smithy.api#xmlName": "imageId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateRoute": { @@ -16820,6 +17301,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateRouteTable": { @@ -16880,6 +17364,9 @@ "smithy.api#xmlName": "routeTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSecurityGroup": { @@ -16901,7 +17388,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

A description for the security group. This is informational only.

\n

Constraints: Up to 255 characters in length

\n

Constraints for EC2-Classic: ASCII characters

\n

Constraints for EC2-VPC: a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=&;{}!$*

", + "smithy.api#documentation": "

A description for the security group.

\n

Constraints: Up to 255 characters in length

\n

Constraints for EC2-Classic: ASCII characters

\n

Constraints for EC2-VPC: a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=&;{}!$*

", "smithy.api#required": {}, "smithy.api#xmlName": "GroupDescription" } @@ -16961,6 +17448,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSnapshot": { @@ -16972,7 +17462,7 @@ "target": "com.amazonaws.ec2#Snapshot" }, "traits": { - "smithy.api#documentation": "

Creates a snapshot of an EBS volume and stores it in Amazon S3. You can use snapshots for\n \tbackups, to make copies of EBS volumes, and to save data before shutting down an\n \tinstance.

\n

You can create snapshots of volumes in a Region and volumes on an Outpost. If you \n \tcreate a snapshot of a volume in a Region, the snapshot must be stored in the same \n \tRegion as the volume. If you create a snapshot of a volume on an Outpost, the snapshot \n \tcan be stored on the same Outpost as the volume, or in the Region for that Outpost.

\n

When a snapshot is created, any Amazon Web Services Marketplace product codes that are associated with the\n source volume are propagated to the snapshot.

\n

You can take a snapshot of an attached volume that is in use. However, snapshots only\n capture data that has been written to your Amazon EBS volume at the time the snapshot command is\n issued; this might exclude any data that has been cached by any applications or the operating\n system. If you can pause any file systems on the volume long enough to take a snapshot, your\n snapshot should be complete. However, if you cannot pause all file writes to the volume, you\n should unmount the volume from within the instance, issue the snapshot command, and then\n remount the volume to ensure a consistent and complete snapshot. You may remount and use your\n volume while the snapshot status is pending.

\n

To create a snapshot for Amazon EBS volumes that serve as root devices, you should stop the\n instance before taking the snapshot.

\n

Snapshots that are taken from encrypted volumes are automatically encrypted. Volumes that\n are created from encrypted snapshots are also automatically encrypted. Your encrypted volumes\n and any associated snapshots always remain protected.

\n

You can tag your snapshots during creation. For more information, see Tag your Amazon EC2\n resources in the Amazon Elastic Compute Cloud User Guide.

\n

For more information, see Amazon Elastic Block Store and Amazon EBS encryption in the Amazon Elastic Compute Cloud User Guide.

" + "smithy.api#documentation": "

Creates a snapshot of an EBS volume and stores it in Amazon S3. You can use snapshots for\n \tbackups, to make copies of EBS volumes, and to save data before shutting down an\n \tinstance.

\n

You can create snapshots of volumes in a Region and volumes on an Outpost. If you \n \tcreate a snapshot of a volume in a Region, the snapshot must be stored in the same \n \tRegion as the volume. If you create a snapshot of a volume on an Outpost, the snapshot \n \tcan be stored on the same Outpost as the volume, or in the Region for that Outpost.

\n

When a snapshot is created, any Amazon Web Services Marketplace product codes that are associated with the\n source volume are propagated to the snapshot.

\n

You can take a snapshot of an attached volume that is in use. However, snapshots only\n capture data that has been written to your Amazon EBS volume at the time the snapshot command is\n issued; this might exclude any data that has been cached by any applications or the operating\n system. If you can pause any file systems on the volume long enough to take a snapshot, your\n snapshot should be complete. However, if you cannot pause all file writes to the volume, you\n should unmount the volume from within the instance, issue the snapshot command, and then\n remount the volume to ensure a consistent and complete snapshot. You may remount and use your\n volume while the snapshot status is pending.

\n

When you create a snapshot for an EBS volume that serves as a root device, we recommend \n that you stop the instance before taking the snapshot.

\n

Snapshots that are taken from encrypted volumes are automatically encrypted. Volumes that\n are created from encrypted snapshots are also automatically encrypted. Your encrypted volumes\n and any associated snapshots always remain protected.

\n

You can tag your snapshots during creation. For more information, see Tag your Amazon EC2\n resources in the Amazon Elastic Compute Cloud User Guide.

\n

For more information, see Amazon Elastic Block Store and Amazon EBS encryption in the Amazon Elastic Compute Cloud User Guide.

" } }, "com.amazonaws.ec2#CreateSnapshotRequest": { @@ -17092,6 +17582,9 @@ "smithy.api#xmlName": "snapshotSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSpotDatafeedSubscription": { @@ -17156,7 +17649,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateSpotDatafeedSubscription.

" + "smithy.api#documentation": "

Contains the output of CreateSpotDatafeedSubscription.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateStoreImageTask": { @@ -17221,6 +17715,9 @@ "smithy.api#xmlName": "objectKey" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSubnet": { @@ -17311,6 +17808,9 @@ "smithy.api#xmlName": "subnetCidrReservation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSubnetRequest": { @@ -17395,6 +17895,9 @@ "smithy.api#xmlName": "subnet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTags": { @@ -17512,6 +18015,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTrafficMirrorFilterRule": { @@ -17643,6 +18149,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTrafficMirrorSession": { @@ -17761,6 +18270,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTrafficMirrorTarget": { @@ -17848,6 +18360,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGateway": { @@ -17956,6 +18471,9 @@ "smithy.api#xmlName": "transitGatewayConnectPeer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayConnectRequest": { @@ -18024,6 +18542,9 @@ "smithy.api#xmlName": "transitGatewayConnect" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayMulticastDomain": { @@ -18112,6 +18633,9 @@ "smithy.api#xmlName": "transitGatewayMulticastDomain" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayPeeringAttachment": { @@ -18212,6 +18736,9 @@ "smithy.api#xmlName": "transitGatewayPeeringAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayPolicyTable": { @@ -18267,6 +18794,9 @@ "smithy.api#xmlName": "transitGatewayPolicyTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayPrefixListReference": { @@ -18338,6 +18868,9 @@ "smithy.api#xmlName": "transitGatewayPrefixListReference" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayRequest": { @@ -18386,6 +18919,9 @@ "smithy.api#xmlName": "transitGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayRoute": { @@ -18457,6 +18993,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayRouteTable": { @@ -18533,6 +19072,9 @@ "smithy.api#xmlName": "transitGatewayRouteTableAnnouncement" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayRouteTableRequest": { @@ -18576,6 +19118,9 @@ "smithy.api#xmlName": "transitGatewayRouteTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayVpcAttachment": { @@ -18679,6 +19224,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVerifiedAccessEndpoint": { @@ -18718,7 +19266,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for a network interface-type endpoint.

" + "smithy.api#documentation": "

Describes the network interface options when creating an Amazon Web Services Verified Access endpoint using the\n network-interface type.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessEndpointLoadBalancerOptions": { @@ -18753,7 +19301,7 @@ } }, "traits": { - "smithy.api#documentation": "

Describes a load balancer when creating an Amazon Web Services Verified Access endpoint using the\n load-balancer type.

" + "smithy.api#documentation": "

Describes the load balancer options when creating an Amazon Web Services Verified Access endpoint using the\n load-balancer type.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessEndpointRequest": { @@ -18771,7 +19319,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointType", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The type of Amazon Web Services Verified Access endpoint to create.

", + "smithy.api#documentation": "

The type of Verified Access endpoint to create.

", "smithy.api#required": {} } }, @@ -18779,7 +19327,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointAttachmentType", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The Amazon Web Services network component Verified Access attaches to.

", + "smithy.api#documentation": "

The type of attachment.

", "smithy.api#required": {} } }, @@ -18803,45 +19351,45 @@ "target": "com.amazonaws.ec2#String", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

A custom identifier that gets prepended to a DNS name that is generated for the endpoint.

", + "smithy.api#documentation": "

A custom identifier that is prepended to the DNS name that is generated for the\n endpoint.

", "smithy.api#required": {} } }, "SecurityGroupIds": { "target": "com.amazonaws.ec2#SecurityGroupIdList", "traits": { - "smithy.api#documentation": "

The Amazon EC2 security groups to associate with the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The IDs of the security groups to associate with the Verified Access endpoint.

", "smithy.api#xmlName": "SecurityGroupId" } }, "LoadBalancerOptions": { "target": "com.amazonaws.ec2#CreateVerifiedAccessEndpointLoadBalancerOptions", "traits": { - "smithy.api#documentation": "

The load balancer details if creating the Amazon Web Services Verified Access endpoint as\n load-balancertype.

" + "smithy.api#documentation": "

The load balancer details. This parameter is required if the endpoint type is\n load-balancer.

" } }, "NetworkInterfaceOptions": { "target": "com.amazonaws.ec2#CreateVerifiedAccessEndpointEniOptions", "traits": { - "smithy.api#documentation": "

The network interface details if creating the Amazon Web Services Verified Access endpoint as\n network-interfacetype.

" + "smithy.api#documentation": "

The network interface details. This parameter is required if the endpoint type is\n network-interface.

" } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access endpoint.

" + "smithy.api#documentation": "

A description for the Verified Access endpoint.

" } }, "PolicyDocument": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

" + "smithy.api#documentation": "

The Verified Access policy document.

" } }, "TagSpecifications": { "target": "com.amazonaws.ec2#TagSpecificationList", "traits": { - "smithy.api#documentation": "

The tags to assign to the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The tags to assign to the Verified Access endpoint.

", "smithy.api#xmlName": "TagSpecification" } }, @@ -18872,10 +19420,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpoint", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessEndpoint", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#xmlName": "verifiedAccessEndpoint" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVerifiedAccessEndpointSubnetIdList": { @@ -18896,7 +19447,7 @@ "target": "com.amazonaws.ec2#CreateVerifiedAccessGroupResult" }, "traits": { - "smithy.api#documentation": "

An Amazon Web Services Verified Access group is a collection of Amazon Web Services Verified Access endpoints who's associated applications have\n similar security requirements. Each instance within an Amazon Web Services Verified Access group shares an Amazon Web Services Verified Access policy. For\n example, you can group all Amazon Web Services Verified Access instances associated with “sales” applications together and\n use one common Amazon Web Services Verified Access policy.

" + "smithy.api#documentation": "

An Amazon Web Services Verified Access group is a collection of Amazon Web Services Verified Access endpoints who's associated applications have\n similar security requirements. Each instance within a Verified Access group shares an Verified Access policy. For\n example, you can group all Verified Access instances associated with \"sales\" applications together and\n use one common Verified Access policy.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessGroupRequest": { @@ -18906,26 +19457,26 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access group.

" + "smithy.api#documentation": "

A description for the Verified Access group.

" } }, "PolicyDocument": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

" + "smithy.api#documentation": "

The Verified Access policy document.

" } }, "TagSpecifications": { "target": "com.amazonaws.ec2#TagSpecificationList", "traits": { - "smithy.api#documentation": "

The tags to assign to the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The tags to assign to the Verified Access group.

", "smithy.api#xmlName": "TagSpecification" } }, @@ -18960,6 +19511,9 @@ "smithy.api#xmlName": "verifiedAccessGroup" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVerifiedAccessInstance": { @@ -18980,13 +19534,13 @@ "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

A description for the Verified Access instance.

" } }, "TagSpecifications": { "target": "com.amazonaws.ec2#TagSpecificationList", "traits": { - "smithy.api#documentation": "

The tags to assign to the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The tags to assign to the Verified Access instance.

", "smithy.api#xmlName": "TagSpecification" } }, @@ -19017,10 +19571,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVerifiedAccessTrustProvider": { @@ -19032,7 +19589,7 @@ "target": "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderResult" }, "traits": { - "smithy.api#documentation": "

A trust provider is a third-party entity that creates, maintains, and manages identity\n information for users and devices. When an application request is made, the identity\n information sent by the trust provider will be evaluated by Amazon Web Services Verified Access, before allowing or\n denying the application request.

" + "smithy.api#documentation": "

A trust provider is a third-party entity that creates, maintains, and manages identity\n information for users and devices. When an application request is made, the identity\n information sent by the trust provider is evaluated by Verified Access before allowing or\n denying the application request.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderDeviceOptions": { @@ -19046,7 +19603,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for a device-identity type trust provider.

" + "smithy.api#documentation": "

Describes the options when creating an Amazon Web Services Verified Access trust provider using the\n device type.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderOidcOptions": { @@ -19083,7 +19640,7 @@ } }, "ClientSecret": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#ClientSecretType", "traits": { "smithy.api#documentation": "

The client secret.

" } @@ -19096,7 +19653,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for an OIDC-based, user-identity type trust provider.

" + "smithy.api#documentation": "

Describes the options when creating an Amazon Web Services Verified Access trust provider using the user\n type.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderRequest": { @@ -19106,32 +19663,32 @@ "target": "com.amazonaws.ec2#TrustProviderType", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The type of trust provider can be either user or device-based.

", + "smithy.api#documentation": "

The type of trust provider.

", "smithy.api#required": {} } }, "UserTrustProviderType": { "target": "com.amazonaws.ec2#UserTrustProviderType", "traits": { - "smithy.api#documentation": "

The type of user-based trust provider.

" + "smithy.api#documentation": "

The type of user-based trust provider. This parameter is required when the provider type\n is user.

" } }, "DeviceTrustProviderType": { "target": "com.amazonaws.ec2#DeviceTrustProviderType", "traits": { - "smithy.api#documentation": "

The type of device-based trust provider.

" + "smithy.api#documentation": "

The type of device-based trust provider. This parameter is required when the provider\n type is device.

" } }, "OidcOptions": { "target": "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderOidcOptions", "traits": { - "smithy.api#documentation": "

The OpenID Connect details for an oidc-type, user-identity based trust provider.

" + "smithy.api#documentation": "

The options for a OpenID Connect-compatible user-identity trust provider. This parameter\n is required when the provider type is user.

" } }, "DeviceOptions": { "target": "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderDeviceOptions", "traits": { - "smithy.api#documentation": "

The options for device identity based trust providers.

" + "smithy.api#documentation": "

The options for a device-based trust provider. This parameter is required when the\n provider type is device.

" } }, "PolicyReferenceName": { @@ -19145,13 +19702,13 @@ "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access trust provider.

" + "smithy.api#documentation": "

A description for the Verified Access trust provider.

" } }, "TagSpecifications": { "target": "com.amazonaws.ec2#TagSpecificationList", "traits": { - "smithy.api#documentation": "

The tags to assign to the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The tags to assign to the Verified Access trust provider.

", "smithy.api#xmlName": "TagSpecification" } }, @@ -19182,10 +19739,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVolume": { @@ -19257,7 +19817,7 @@ "type": "structure", "members": { "AvailabilityZone": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#AvailabilityZoneName", "traits": { "smithy.api#clientOptional": {}, "smithy.api#documentation": "

The Availability Zone in which to create the volume.

", @@ -19464,6 +20024,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpcEndpointRequest": { @@ -19583,6 +20146,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpcEndpointServiceConfiguration": { @@ -19680,6 +20246,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpcPeeringConnection": { @@ -19762,6 +20331,9 @@ "smithy.api#xmlName": "vpcPeeringConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpcRequest": { @@ -19866,6 +20438,9 @@ "smithy.api#xmlName": "vpc" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpnConnection": { @@ -19955,7 +20530,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateVpnConnection.

" + "smithy.api#documentation": "

Contains the output of CreateVpnConnection.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpnConnectionRoute": { @@ -20068,7 +20644,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateVpnGateway.

" + "smithy.api#documentation": "

Contains the output of CreateVpnGateway.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreditSpecification": { @@ -20490,6 +21067,9 @@ "smithy.api#xmlName": "carrierGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteClientVpnEndpoint": { @@ -20539,6 +21119,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteClientVpnRoute": { @@ -20602,6 +21185,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteCoipCidr": { @@ -20659,6 +21245,9 @@ "smithy.api#xmlName": "coipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteCoipPool": { @@ -20708,6 +21297,9 @@ "smithy.api#xmlName": "coipPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteCustomerGateway": { @@ -20836,6 +21428,9 @@ "smithy.api#xmlName": "returnCode" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteFleetError": { @@ -21030,6 +21625,9 @@ "smithy.api#xmlName": "unsuccessfulFleetDeletionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteFlowLogs": { @@ -21080,6 +21678,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteFpgaImage": { @@ -21131,6 +21732,61 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.ec2#DeleteInstanceConnectEndpoint": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#DeleteInstanceConnectEndpointRequest" + }, + "output": { + "target": "com.amazonaws.ec2#DeleteInstanceConnectEndpointResult" + }, + "traits": { + "smithy.api#documentation": "

Deletes the specified EC2 Instance Connect Endpoint.

" + } + }, + "com.amazonaws.ec2#DeleteInstanceConnectEndpointRequest": { + "type": "structure", + "members": { + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, \n and provides an error response. If you have the required permissions, the error response is DryRunOperation. \n Otherwise, it is UnauthorizedOperation.

" + } + }, + "InstanceConnectEndpointId": { + "target": "com.amazonaws.ec2#InstanceConnectEndpointId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The ID of the EC2 Instance Connect Endpoint to delete.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#DeleteInstanceConnectEndpointResult": { + "type": "structure", + "members": { + "InstanceConnectEndpoint": { + "target": "com.amazonaws.ec2#Ec2InstanceConnectEndpoint", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpoint", + "smithy.api#documentation": "

Information about the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "instanceConnectEndpoint" + } + } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteInstanceEventWindow": { @@ -21189,6 +21845,9 @@ "smithy.api#xmlName": "instanceEventWindowState" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteInternetGateway": { @@ -21290,6 +21949,9 @@ "smithy.api#xmlName": "ipamPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteIpamRequest": { @@ -21371,6 +22033,9 @@ "smithy.api#xmlName": "ipamResourceDiscovery" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteIpamResult": { @@ -21384,6 +22049,9 @@ "smithy.api#xmlName": "ipam" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteIpamScope": { @@ -21433,6 +22101,9 @@ "smithy.api#xmlName": "ipamScope" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteKeyPair": { @@ -21528,6 +22199,9 @@ "smithy.api#xmlName": "launchTemplate" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLaunchTemplateVersions": { @@ -21692,6 +22366,9 @@ "smithy.api#xmlName": "unsuccessfullyDeletedLaunchTemplateVersionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLocalGatewayRoute": { @@ -21753,6 +22430,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLocalGatewayRouteTable": { @@ -21802,6 +22482,9 @@ "smithy.api#xmlName": "localGatewayRouteTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLocalGatewayRouteTableVirtualInterfaceGroupAssociation": { @@ -21851,6 +22534,9 @@ "smithy.api#xmlName": "localGatewayRouteTableVirtualInterfaceGroupAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLocalGatewayRouteTableVpcAssociation": { @@ -21900,6 +22586,9 @@ "smithy.api#xmlName": "localGatewayRouteTableVpcAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteManagedPrefixList": { @@ -21949,6 +22638,9 @@ "smithy.api#xmlName": "prefixList" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNatGateway": { @@ -21999,6 +22691,9 @@ "smithy.api#xmlName": "natGatewayId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkAcl": { @@ -22162,6 +22857,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeAnalysisId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInsightsAccessScopeRequest": { @@ -22199,6 +22897,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInsightsAnalysis": { @@ -22248,6 +22949,9 @@ "smithy.api#xmlName": "networkInsightsAnalysisId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInsightsPath": { @@ -22297,6 +23001,9 @@ "smithy.api#xmlName": "networkInsightsPathId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInterface": { @@ -22371,7 +23078,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output for DeleteNetworkInterfacePermission.

" + "smithy.api#documentation": "

Contains the output for DeleteNetworkInterfacePermission.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInterfaceRequest": { @@ -22492,6 +23200,9 @@ "smithy.api#xmlName": "returnValue" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteQueuedReservedInstances": { @@ -22612,6 +23323,9 @@ "smithy.api#xmlName": "failedQueuedPurchaseDeletionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteRoute": { @@ -22886,6 +23600,9 @@ "smithy.api#xmlName": "deletedSubnetCidrReservation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteSubnetRequest": { @@ -23009,6 +23726,9 @@ "smithy.api#xmlName": "trafficMirrorFilterId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTrafficMirrorFilterRule": { @@ -23058,6 +23778,9 @@ "smithy.api#xmlName": "trafficMirrorFilterRuleId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTrafficMirrorSession": { @@ -23107,6 +23830,9 @@ "smithy.api#xmlName": "trafficMirrorSessionId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTrafficMirrorTarget": { @@ -23156,6 +23882,9 @@ "smithy.api#xmlName": "trafficMirrorTargetId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGateway": { @@ -23229,6 +23958,9 @@ "smithy.api#xmlName": "transitGatewayConnectPeer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayConnectRequest": { @@ -23266,6 +23998,9 @@ "smithy.api#xmlName": "transitGatewayConnect" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayMulticastDomain": { @@ -23315,6 +24050,9 @@ "smithy.api#xmlName": "transitGatewayMulticastDomain" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayPeeringAttachment": { @@ -23364,6 +24102,9 @@ "smithy.api#xmlName": "transitGatewayPeeringAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayPolicyTable": { @@ -23413,6 +24154,9 @@ "smithy.api#xmlName": "transitGatewayPolicyTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayPrefixListReference": { @@ -23470,6 +24214,9 @@ "smithy.api#xmlName": "transitGatewayPrefixListReference" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayRequest": { @@ -23507,6 +24254,9 @@ "smithy.api#xmlName": "transitGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayRoute": { @@ -23564,6 +24314,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayRouteTable": { @@ -23625,6 +24378,9 @@ "smithy.api#xmlName": "transitGatewayRouteTableAnnouncement" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayRouteTableRequest": { @@ -23662,6 +24418,9 @@ "smithy.api#xmlName": "transitGatewayRouteTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayVpcAttachment": { @@ -23711,6 +24470,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVerifiedAccessEndpoint": { @@ -23732,7 +24494,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#required": {} } }, @@ -23763,10 +24525,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpoint", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessEndpoint", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#xmlName": "verifiedAccessEndpoint" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVerifiedAccessGroup": { @@ -23788,7 +24553,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#required": {} } }, @@ -23819,10 +24584,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroup", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessGroup", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#xmlName": "verifiedAccessGroup" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVerifiedAccessInstance": { @@ -23844,7 +24612,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, @@ -23875,10 +24643,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVerifiedAccessTrustProvider": { @@ -23900,7 +24671,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#required": {} } }, @@ -23931,10 +24702,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVolume": { @@ -24035,6 +24809,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVpcEndpointServiceConfigurations": { @@ -24085,6 +24862,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVpcEndpoints": { @@ -24135,6 +24915,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVpcPeeringConnection": { @@ -24190,6 +24973,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVpcRequest": { @@ -24380,6 +25166,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeprovisionIpamPoolCidr": { @@ -24435,6 +25224,9 @@ "smithy.api#xmlName": "ipamPoolCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeprovisionPublicIpv4PoolCidr": { @@ -24500,6 +25292,9 @@ "smithy.api#xmlName": "deprovisionedAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeprovisionedAddressSet": { @@ -24576,7 +25371,9 @@ "InstanceTagAttribute": { "target": "com.amazonaws.ec2#DeregisterInstanceTagAttributeRequest", "traits": { - "smithy.api#documentation": "

Information about the tag keys to deregister.

" + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

Information about the tag keys to deregister.

", + "smithy.api#required": {} } } }, @@ -24595,6 +25392,9 @@ "smithy.api#xmlName": "instanceTagAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeregisterInstanceTagAttributeRequest": { @@ -24677,6 +25477,9 @@ "smithy.api#xmlName": "deregisteredMulticastGroupMembers" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeregisterTransitGatewayMulticastGroupSources": { @@ -24736,6 +25539,9 @@ "smithy.api#xmlName": "deregisteredMulticastGroupSources" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAccountAttributes": { @@ -24787,6 +25593,9 @@ "smithy.api#xmlName": "accountAttributeSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAddressTransfers": { @@ -24798,7 +25607,7 @@ "target": "com.amazonaws.ec2#DescribeAddressTransfersResult" }, "traits": { - "smithy.api#documentation": "

Describes an Elastic IP address transfer. For more information, see Transfer Elastic IP addresses in the Amazon Virtual Private Cloud User Guide.

", + "smithy.api#documentation": "

Describes an Elastic IP address transfer. For more information, see Transfer Elastic IP addresses in the Amazon Virtual Private Cloud User Guide.

\n

When you transfer an Elastic IP address, there is a two-step handshake\n between the source and transfer Amazon Web Services accounts. When the source account starts the transfer,\n the transfer account has seven days to accept the Elastic IP address\n transfer. During those seven days, the source account can view the\n pending transfer by using this action. After seven days, the\n transfer expires and ownership of the Elastic IP\n address returns to the source\n account. Accepted transfers are visible to the source account for three days\n after the transfers have been accepted.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -24873,6 +25682,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAddresses": { @@ -24884,7 +25696,7 @@ "target": "com.amazonaws.ec2#DescribeAddressesResult" }, "traits": { - "smithy.api#documentation": "

Describes the specified Elastic IP addresses or all of your Elastic IP addresses.

\n

An Elastic IP address is for use in either the EC2-Classic platform or in a VPC.\n\t\t\t\tFor more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Describes the specified Elastic IP addresses or all of your Elastic IP addresses.

" } }, "com.amazonaws.ec2#DescribeAddressesAttribute": { @@ -24967,6 +25779,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAddressesRequest": { @@ -24975,7 +25790,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters. Filter names and values are case-sensitive.

\n
    \n
  • \n

    \n allocation-id - [EC2-VPC] The allocation ID for the address.

    \n
  • \n
  • \n

    \n association-id - [EC2-VPC] The association ID for the address.

    \n
  • \n
  • \n

    \n domain - Indicates whether the address is for use in EC2-Classic (standard) \n or in a VPC (vpc).

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance the address is associated with, if any.

    \n
  • \n
  • \n

    \n network-border-group - A unique set of Availability Zones, Local Zones,\n or Wavelength Zones from where Amazon Web Services advertises IP addresses.

    \n
  • \n
  • \n

    \n network-interface-id - [EC2-VPC] The ID of the network interface that the address is associated with, if any.

    \n
  • \n
  • \n

    \n network-interface-owner-id - The Amazon Web Services account ID of the owner.

    \n
  • \n
  • \n

    \n private-ip-address - [EC2-VPC] The private IP address associated with the Elastic IP address.

    \n
  • \n
  • \n

    \n public-ip - The Elastic IP address, or the carrier IP address.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters. Filter names and values are case-sensitive.

\n
    \n
  • \n

    \n allocation-id - The allocation ID for the address.

    \n
  • \n
  • \n

    \n association-id - The association ID for the address.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance the address is associated with, if any.

    \n
  • \n
  • \n

    \n network-border-group - A unique set of Availability Zones, Local Zones,\n or Wavelength Zones from where Amazon Web Services advertises IP addresses.

    \n
  • \n
  • \n

    \n network-interface-id - The ID of the network interface that the address is associated with, if any.

    \n
  • \n
  • \n

    \n network-interface-owner-id - The Amazon Web Services account ID of the owner.

    \n
  • \n
  • \n

    \n private-ip-address - The private IP address associated with the Elastic IP address.

    \n
  • \n
  • \n

    \n public-ip - The Elastic IP address, or the carrier IP address.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -24989,7 +25804,7 @@ "AllocationIds": { "target": "com.amazonaws.ec2#AllocationIdList", "traits": { - "smithy.api#documentation": "

[EC2-VPC] Information about the allocation IDs.

", + "smithy.api#documentation": "

Information about the allocation IDs.

", "smithy.api#xmlName": "AllocationId" } }, @@ -25019,6 +25834,9 @@ "smithy.api#xmlName": "addressesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAggregateIdFormat": { @@ -25070,6 +25888,9 @@ "smithy.api#xmlName": "statusSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAvailabilityZones": { @@ -25142,6 +25963,9 @@ "smithy.api#xmlName": "availabilityZoneInfo" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAwsNetworkPerformanceMetricSubscriptions": { @@ -25218,6 +26042,9 @@ "smithy.api#xmlName": "subscriptionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeBundleTasks": { @@ -25302,6 +26129,9 @@ "smithy.api#xmlName": "bundleInstanceTasksSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeByoipCidrs": { @@ -25382,6 +26212,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCapacityReservationFleets": { @@ -25475,6 +26308,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCapacityReservations": { @@ -25568,6 +26404,9 @@ "smithy.api#xmlName": "capacityReservationSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCarrierGateways": { @@ -25651,6 +26490,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClassicLinkInstances": { @@ -25750,6 +26592,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnAuthorizationRules": { @@ -25844,6 +26689,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnConnections": { @@ -25938,6 +26786,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnEndpointMaxResults": { @@ -26031,6 +26882,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnRoutes": { @@ -26125,6 +26979,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnTargetNetworks": { @@ -26225,6 +27082,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCoipPools": { @@ -26308,6 +27168,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeConversionTaskList": { @@ -26435,6 +27298,9 @@ "smithy.api#xmlName": "conversionTasks" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCustomerGateways": { @@ -26532,7 +27398,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeCustomerGateways.

" + "smithy.api#documentation": "

Contains the output of DescribeCustomerGateways.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeDhcpOptions": { @@ -26628,6 +27495,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeEgressOnlyInternetGateways": { @@ -26721,6 +27591,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeElasticGpus": { @@ -26818,6 +27691,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeExportImageTasks": { @@ -26911,6 +27787,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeExportTasks": { @@ -26991,6 +27870,9 @@ "smithy.api#xmlName": "exportTaskSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFastLaunchImages": { @@ -27081,6 +27963,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFastLaunchImagesSuccessItem": { @@ -27360,6 +28245,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFleetError": { @@ -27509,6 +28397,9 @@ "smithy.api#xmlName": "startTime" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFleetInstances": { @@ -27595,6 +28486,9 @@ "smithy.api#xmlName": "fleetId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFleets": { @@ -27744,6 +28638,9 @@ "smithy.api#xmlName": "fleetSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFlowLogs": { @@ -27826,6 +28723,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFpgaImageAttribute": { @@ -27883,6 +28783,9 @@ "smithy.api#xmlName": "fpgaImageAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFpgaImages": { @@ -27983,6 +28886,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeHostReservationOfferings": { @@ -28072,6 +28978,9 @@ "smithy.api#xmlName": "offeringSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeHostReservations": { @@ -28155,6 +29064,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeHosts": { @@ -28236,6 +29148,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIamInstanceProfileAssociations": { @@ -28321,6 +29236,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIdFormat": { @@ -28360,6 +29278,9 @@ "smithy.api#xmlName": "statusSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIdentityIdFormat": { @@ -28411,6 +29332,9 @@ "smithy.api#xmlName": "statusSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeImageAttribute": { @@ -28616,6 +29540,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeImportImageTasks": { @@ -28699,6 +29626,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeImportSnapshotTasks": { @@ -28809,6 +29739,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceAttribute": { @@ -28861,6 +29794,92 @@ "smithy.api#input": {} } }, + "com.amazonaws.ec2#DescribeInstanceConnectEndpoints": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#DescribeInstanceConnectEndpointsRequest" + }, + "output": { + "target": "com.amazonaws.ec2#DescribeInstanceConnectEndpointsResult" + }, + "traits": { + "smithy.api#documentation": "

Describes the specified EC2 Instance Connect Endpoints or all EC2 Instance Connect Endpoints.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "items": "InstanceConnectEndpoints", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.ec2#DescribeInstanceConnectEndpointsRequest": { + "type": "structure", + "members": { + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, \n and provides an error response. If you have the required permissions, the error response is DryRunOperation. \n Otherwise, it is UnauthorizedOperation.

" + } + }, + "MaxResults": { + "target": "com.amazonaws.ec2#InstanceConnectEndpointMaxResults", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The maximum number of items to return for this request.\n To get the next page of items, make another request with the token returned in the output.\n\t For more information, see Pagination.

" + } + }, + "NextToken": { + "target": "com.amazonaws.ec2#NextToken", + "traits": { + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" + } + }, + "Filters": { + "target": "com.amazonaws.ec2#FilterList", + "traits": { + "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n instance-connect-endpoint-id - The ID of the EC2 Instance Connect Endpoint.

    \n
  • \n
  • \n

    \n state - The state of the EC2 Instance Connect Endpoint (create-in-progress | create-complete | create-failed | \n delete-in-progress | delete-complete | delete-failed).

    \n
  • \n
  • \n

    \n subnet-id - The ID of the subnet in which the EC2 Instance\n Connect Endpoint was created.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n tag-value - The value of a tag assigned to the resource. Use this filter to find all resources \n that have a tag with a specific value, regardless of tag key.

    \n
  • \n
  • \n

    \n vpc-id - The ID of the VPC in which the EC2 Instance Connect\n Endpoint was created.

    \n
  • \n
", + "smithy.api#xmlName": "Filter" + } + }, + "InstanceConnectEndpointIds": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "smithy.api#documentation": "

One or more EC2 Instance Connect Endpoint IDs.

", + "smithy.api#xmlName": "InstanceConnectEndpointId" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#DescribeInstanceConnectEndpointsResult": { + "type": "structure", + "members": { + "InstanceConnectEndpoints": { + "target": "com.amazonaws.ec2#InstanceConnectEndpointSet", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpointSet", + "smithy.api#documentation": "

Information about the EC2 Instance Connect Endpoints.

", + "smithy.api#xmlName": "instanceConnectEndpointSet" + } + }, + "NextToken": { + "target": "com.amazonaws.ec2#NextToken", + "traits": { + "aws.protocols#ec2QueryName": "NextToken", + "smithy.api#documentation": "

The token to include in another request to get the next page of items. This value is null when there\n are no more items to return.

", + "smithy.api#xmlName": "nextToken" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, "com.amazonaws.ec2#DescribeInstanceCreditSpecifications": { "type": "operation", "input": { @@ -28952,6 +29971,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceEventNotificationAttributes": { @@ -28993,6 +30015,9 @@ "smithy.api#xmlName": "instanceTagAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceEventWindows": { @@ -29077,6 +30102,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceStatus": { @@ -29213,6 +30241,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceTypeOfferings": { @@ -29293,6 +30324,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceTypes": { @@ -29334,7 +30368,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters. Filter names and values are case-sensitive.

\n
    \n
  • \n

    \n auto-recovery-supported - Indicates whether auto recovery is supported (true | false).

    \n
  • \n
  • \n

    \n bare-metal - Indicates whether it is a bare metal instance type (true | false).

    \n
  • \n
  • \n

    \n burstable-performance-supported - Indicates whether it is a burstable\n performance instance type (true | false).

    \n
  • \n
  • \n

    \n current-generation - Indicates whether this instance type is the latest\n generation instance type of an instance family (true | false).

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-bandwidth-in-mbps - The baseline\n bandwidth performance for an EBS-optimized instance type, in Mbps.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-iops - The baseline input/output storage\n operations per second for an EBS-optimized instance type.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-throughput-in-mbps - The baseline\n throughput performance for an EBS-optimized instance type, in MB/s.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-bandwidth-in-mbps - The maximum bandwidth\n performance for an EBS-optimized instance type, in Mbps.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-iops - The maximum input/output storage\n operations per second for an EBS-optimized instance type.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-throughput-in-mbps - The maximum\n throughput performance for an EBS-optimized instance type, in MB/s.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-support - Indicates whether the instance type is\n EBS-optimized (supported | unsupported |\n default).

    \n
  • \n
  • \n

    \n ebs-info.encryption-support - Indicates whether EBS encryption is supported\n (supported | unsupported).

    \n
  • \n
  • \n

    \n ebs-info.nvme-support - Indicates whether non-volatile memory express (NVMe)\n is supported for EBS volumes (required | supported | unsupported).

    \n
  • \n
  • \n

    \n free-tier-eligible - Indicates whether the instance type is eligible to use\n in the free tier (true | false).

    \n
  • \n
  • \n

    \n hibernation-supported - Indicates whether On-Demand hibernation is supported (true | false).

    \n
  • \n
  • \n

    \n hypervisor - The hypervisor (nitro | xen).

    \n
  • \n
  • \n

    \n instance-storage-info.disk.count - The number of local disks.

    \n
  • \n
  • \n

    \n instance-storage-info.disk.size-in-gb - The storage size of each instance storage disk, in\n GB.

    \n
  • \n
  • \n

    \n instance-storage-info.disk.type - The storage technology for the local\n instance storage disks (hdd | ssd).

    \n
  • \n
  • \n

    \n instance-storage-info.encryption-support - Indicates whether data is encrypted at rest \n (required | supported | unsupported).

    \n
  • \n
  • \n

    \n instance-storage-info.nvme-support - Indicates whether non-volatile memory\n express (NVMe) is supported for instance store (required | supported |\n unsupported).

    \n
  • \n
  • \n

    \n instance-storage-info.total-size-in-gb - The total amount of storage available from all local\n instance storage, in GB.

    \n
  • \n
  • \n

    \n instance-storage-supported - Indicates whether the instance type has local\n instance storage (true | false).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example c5.2xlarge or\n c5*).

    \n
  • \n
  • \n

    \n memory-info.size-in-mib - The memory size.

    \n
  • \n
  • \n

    \n network-info.efa-info.maximum-efa-interfaces - The maximum number of Elastic \n Fabric Adapters (EFAs) per instance.

    \n
  • \n
  • \n

    \n network-info.efa-supported - Indicates whether the instance type supports\n Elastic Fabric Adapter (EFA) (true | false).

    \n
  • \n
  • \n

    \n network-info.ena-support - Indicates whether Elastic Network Adapter (ENA) is\n supported or required (required | supported |\n unsupported).

    \n
  • \n
  • \n

    \n network-info.encryption-in-transit-supported - Indicates whether the instance type \n automatically encrypts in-transit traffic between instances (true | false).

    \n
  • \n
  • \n

    \n network-info.ipv4-addresses-per-interface - The maximum number of private IPv4 addresses per\n network interface.

    \n
  • \n
  • \n

    \n network-info.ipv6-addresses-per-interface - The maximum number of private IPv6 addresses per\n network interface.

    \n
  • \n
  • \n

    \n network-info.ipv6-supported - Indicates whether the instance type supports IPv6 (true | false).

    \n
  • \n
  • \n

    \n network-info.maximum-network-cards - The maximum number of network cards per\n instance.

    \n
  • \n
  • \n

    \n network-info.maximum-network-interfaces - The maximum number of network interfaces per instance.

    \n
  • \n
  • \n

    \n network-info.network-performance - The network performance (for example, \"25\n Gigabit\").

    \n
  • \n
  • \n

    \n processor-info.supported-architecture - The CPU architecture\n (arm64 | i386 | x86_64).

    \n
  • \n
  • \n

    \n processor-info.sustained-clock-speed-in-ghz - The CPU clock speed, in GHz.

    \n
  • \n
  • \n

    \n supported-boot-mode - The boot mode (legacy-bios |\n uefi).

    \n
  • \n
  • \n

    \n supported-root-device-type - The root device type (ebs |\n instance-store).

    \n
  • \n
  • \n

    \n supported-usage-class - The usage class (on-demand |\n spot).

    \n
  • \n
  • \n

    \n supported-virtualization-type - The virtualization type (hvm |\n paravirtual).

    \n
  • \n
  • \n

    \n vcpu-info.default-cores - The default number of cores for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.default-threads-per-core - The default number of threads per core for the instance\n type.

    \n
  • \n
  • \n

    \n vcpu-info.default-vcpus - The default number of vCPUs for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.valid-cores - The number of cores that can be configured for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.valid-threads-per-core - The number of threads per core that can be configured for the instance type.\n For example, \"1\" or \"1,2\".

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters. Filter names and values are case-sensitive.

\n
    \n
  • \n

    \n auto-recovery-supported - Indicates whether Amazon CloudWatch action based recovery is supported (true | false).

    \n
  • \n
  • \n

    \n bare-metal - Indicates whether it is a bare metal instance type (true | false).

    \n
  • \n
  • \n

    \n burstable-performance-supported - Indicates whether it is a burstable\n performance instance type (true | false).

    \n
  • \n
  • \n

    \n current-generation - Indicates whether this instance type is the latest\n generation instance type of an instance family (true | false).

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-bandwidth-in-mbps - The baseline\n bandwidth performance for an EBS-optimized instance type, in Mbps.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-iops - The baseline input/output storage\n operations per second for an EBS-optimized instance type.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-throughput-in-mbps - The baseline\n throughput performance for an EBS-optimized instance type, in MB/s.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-bandwidth-in-mbps - The maximum bandwidth\n performance for an EBS-optimized instance type, in Mbps.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-iops - The maximum input/output storage\n operations per second for an EBS-optimized instance type.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-throughput-in-mbps - The maximum\n throughput performance for an EBS-optimized instance type, in MB/s.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-support - Indicates whether the instance type is\n EBS-optimized (supported | unsupported |\n default).

    \n
  • \n
  • \n

    \n ebs-info.encryption-support - Indicates whether EBS encryption is supported\n (supported | unsupported).

    \n
  • \n
  • \n

    \n ebs-info.nvme-support - Indicates whether non-volatile memory express (NVMe)\n is supported for EBS volumes (required | supported | unsupported).

    \n
  • \n
  • \n

    \n free-tier-eligible - Indicates whether the instance type is eligible to use\n in the free tier (true | false).

    \n
  • \n
  • \n

    \n hibernation-supported - Indicates whether On-Demand hibernation is supported (true | false).

    \n
  • \n
  • \n

    \n hypervisor - The hypervisor (nitro | xen).

    \n
  • \n
  • \n

    \n instance-storage-info.disk.count - The number of local disks.

    \n
  • \n
  • \n

    \n instance-storage-info.disk.size-in-gb - The storage size of each instance storage disk, in\n GB.

    \n
  • \n
  • \n

    \n instance-storage-info.disk.type - The storage technology for the local\n instance storage disks (hdd | ssd).

    \n
  • \n
  • \n

    \n instance-storage-info.encryption-support - Indicates whether data is encrypted at rest \n (required | supported | unsupported).

    \n
  • \n
  • \n

    \n instance-storage-info.nvme-support - Indicates whether non-volatile memory\n express (NVMe) is supported for instance store (required | supported |\n unsupported).

    \n
  • \n
  • \n

    \n instance-storage-info.total-size-in-gb - The total amount of storage available from all local\n instance storage, in GB.

    \n
  • \n
  • \n

    \n instance-storage-supported - Indicates whether the instance type has local\n instance storage (true | false).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example c5.2xlarge or\n c5*).

    \n
  • \n
  • \n

    \n memory-info.size-in-mib - The memory size.

    \n
  • \n
  • \n

    \n network-info.efa-info.maximum-efa-interfaces - The maximum number of Elastic \n Fabric Adapters (EFAs) per instance.

    \n
  • \n
  • \n

    \n network-info.efa-supported - Indicates whether the instance type supports\n Elastic Fabric Adapter (EFA) (true | false).

    \n
  • \n
  • \n

    \n network-info.ena-support - Indicates whether Elastic Network Adapter (ENA) is\n supported or required (required | supported |\n unsupported).

    \n
  • \n
  • \n

    \n network-info.encryption-in-transit-supported - Indicates whether the instance type \n automatically encrypts in-transit traffic between instances (true | false).

    \n
  • \n
  • \n

    \n network-info.ipv4-addresses-per-interface - The maximum number of private IPv4 addresses per\n network interface.

    \n
  • \n
  • \n

    \n network-info.ipv6-addresses-per-interface - The maximum number of private IPv6 addresses per\n network interface.

    \n
  • \n
  • \n

    \n network-info.ipv6-supported - Indicates whether the instance type supports IPv6 (true | false).

    \n
  • \n
  • \n

    \n network-info.maximum-network-cards - The maximum number of network cards per\n instance.

    \n
  • \n
  • \n

    \n network-info.maximum-network-interfaces - The maximum number of network interfaces per instance.

    \n
  • \n
  • \n

    \n network-info.network-performance - The network performance (for example, \"25\n Gigabit\").

    \n
  • \n
  • \n

    \n processor-info.supported-architecture - The CPU architecture\n (arm64 | i386 | x86_64).

    \n
  • \n
  • \n

    \n processor-info.sustained-clock-speed-in-ghz - The CPU clock speed, in GHz.

    \n
  • \n
  • \n

    \n supported-boot-mode - The boot mode (legacy-bios |\n uefi).

    \n
  • \n
  • \n

    \n supported-root-device-type - The root device type (ebs |\n instance-store).

    \n
  • \n
  • \n

    \n supported-usage-class - The usage class (on-demand |\n spot).

    \n
  • \n
  • \n

    \n supported-virtualization-type - The virtualization type (hvm |\n paravirtual).

    \n
  • \n
  • \n

    \n vcpu-info.default-cores - The default number of cores for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.default-threads-per-core - The default number of threads per core for the instance\n type.

    \n
  • \n
  • \n

    \n vcpu-info.default-vcpus - The default number of vCPUs for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.valid-cores - The number of cores that can be configured for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.valid-threads-per-core - The number of threads per core that can be configured for the instance type.\n For example, \"1\" or \"1,2\".

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -29374,6 +30408,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstances": { @@ -29547,7 +30584,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n affinity - The affinity setting for an instance running on a\n Dedicated Host (default | host).

    \n
  • \n
  • \n

    \n architecture - The instance architecture (i386 |\n x86_64 | arm64).

    \n
  • \n
  • \n

    \n availability-zone - The Availability Zone of the instance.

    \n
  • \n
  • \n

    \n block-device-mapping.attach-time - The attach time for an EBS\n volume mapped to the instance, for example,\n 2010-09-15T17:15:20.000Z.

    \n
  • \n
  • \n

    \n block-device-mapping.delete-on-termination - A Boolean that\n indicates whether the EBS volume is deleted on instance termination.

    \n
  • \n
  • \n

    \n block-device-mapping.device-name - The device name specified in the\n block device mapping (for example, /dev/sdh or\n xvdh).

    \n
  • \n
  • \n

    \n block-device-mapping.status - The status for the EBS volume\n (attaching | attached | detaching |\n detached).

    \n
  • \n
  • \n

    \n block-device-mapping.volume-id - The volume ID of the EBS\n volume.

    \n
  • \n
  • \n

    \n capacity-reservation-id - The ID of the Capacity Reservation into which the\n instance was launched.

    \n
  • \n
  • \n

    \n client-token - The idempotency token you provided when you launched\n the instance.

    \n
  • \n
  • \n

    \n dns-name - The public DNS name of the instance.

    \n
  • \n
  • \n

    \n group-id - The ID of the security group for the instance.\n EC2-Classic only.

    \n
  • \n
  • \n

    \n group-name - The name of the security group for the instance.\n EC2-Classic only.

    \n
  • \n
  • \n

    \n hibernation-options.configured - A Boolean that indicates whether\n the instance is enabled for hibernation. A value of true means that\n the instance is enabled for hibernation.

    \n
  • \n
  • \n

    \n host-id - The ID of the Dedicated Host on which the instance is\n running, if applicable.

    \n
  • \n
  • \n

    \n hypervisor - The hypervisor type of the instance\n (ovm | xen). The value xen is used\n for both Xen and Nitro hypervisors.

    \n
  • \n
  • \n

    \n iam-instance-profile.arn - The instance profile associated with\n the instance. Specified as an ARN.

    \n
  • \n
  • \n

    \n image-id - The ID of the image used to launch the\n instance.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance.

    \n
  • \n
  • \n

    \n instance-lifecycle - Indicates whether this is a Spot Instance or\n a Scheduled Instance (spot | scheduled).

    \n
  • \n
  • \n

    \n instance-state-code - The state of the instance, as a 16-bit\n unsigned integer. The high byte is used for internal purposes and should be\n ignored. The low byte is set based on the state represented. The valid values\n are: 0 (pending), 16 (running), 32 (shutting-down), 48 (terminated), 64\n (stopping), and 80 (stopped).

    \n
  • \n
  • \n

    \n instance-state-name - The state of the instance\n (pending | running | shutting-down |\n terminated | stopping |\n stopped).

    \n
  • \n
  • \n

    \n instance-type - The type of instance (for example,\n t2.micro).

    \n
  • \n
  • \n

    \n instance.group-id - The ID of the security group for the\n instance.

    \n
  • \n
  • \n

    \n instance.group-name - The name of the security group for the\n instance.

    \n
  • \n
  • \n

    \n ip-address - The public IPv4 address of the instance.

    \n
  • \n
  • \n

    \n kernel-id - The kernel ID.

    \n
  • \n
  • \n

    \n key-name - The name of the key pair used when the instance was\n launched.

    \n
  • \n
  • \n

    \n launch-index - When launching multiple instances, this is the\n index for the instance in the launch group (for example, 0, 1, 2, and so on).\n

    \n
  • \n
  • \n

    \n launch-time - The time when the instance was launched, in the ISO\n 8601 format in the UTC time zone (YYYY-MM-DDThh:mm:ss.sssZ), for example,\n 2021-09-29T11:04:43.305Z. You can use a wildcard\n (*), for example, 2021-09-29T*, which matches an\n entire day.

    \n
  • \n
  • \n

    \n metadata-options.http-tokens - The metadata request authorization\n state (optional | required)

    \n
  • \n
  • \n

    \n metadata-options.http-put-response-hop-limit - The HTTP metadata\n request put response hop limit (integer, possible values 1 to\n 64)

    \n
  • \n
  • \n

    \n metadata-options.http-endpoint - The status of access to the HTTP\n metadata endpoint on your instance (enabled |\n disabled)

    \n
  • \n
  • \n

    \n metadata-options.instance-metadata-tags - The status of access to\n instance tags from the instance metadata (enabled |\n disabled)

    \n
  • \n
  • \n

    \n monitoring-state - Indicates whether detailed monitoring is\n enabled (disabled | enabled).

    \n
  • \n
  • \n

    \n network-interface.addresses.private-ip-address - The private IPv4\n address associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.addresses.primary - Specifies whether the IPv4\n address of the network interface is the primary private IPv4 address.

    \n
  • \n
  • \n

    \n network-interface.addresses.association.public-ip - The ID of the\n association of an Elastic IP address (IPv4) with a network interface.

    \n
  • \n
  • \n

    \n network-interface.addresses.association.ip-owner-id - The owner\n ID of the private IPv4 address associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.public-ip - The address of the\n Elastic IP address (IPv4) bound to the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.ip-owner-id - The owner of the\n Elastic IP address (IPv4) associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.allocation-id - The allocation ID\n returned when you allocated the Elastic IP address (IPv4) for your network\n interface.

    \n
  • \n
  • \n

    \n network-interface.association.association-id - The association ID\n returned when the network interface was associated with an IPv4 address.

    \n
  • \n
  • \n

    \n network-interface.attachment.attachment-id - The ID of the\n interface attachment.

    \n
  • \n
  • \n

    \n network-interface.attachment.instance-id - The ID of the instance\n to which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.instance-owner-id - The owner ID of\n the instance to which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.device-index - The device index to\n which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.status - The status of the\n attachment (attaching | attached |\n detaching | detached).

    \n
  • \n
  • \n

    \n network-interface.attachment.attach-time - The time that the\n network interface was attached to an instance.

    \n
  • \n
  • \n

    \n network-interface.attachment.delete-on-termination - Specifies\n whether the attachment is deleted when an instance is terminated.

    \n
  • \n
  • \n

    \n network-interface.availability-zone - The Availability Zone for\n the network interface.

    \n
  • \n
  • \n

    \n network-interface.description - The description of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.group-id - The ID of a security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.group-name - The name of a security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.ipv6-addresses.ipv6-address - The IPv6 address\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.mac-address - The MAC address of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.network-interface-id - The ID of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.owner-id - The ID of the owner of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.private-dns-name - The private DNS name of the\n network interface.

    \n
  • \n
  • \n

    \n network-interface.requester-id - The requester ID for the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.requester-managed - Indicates whether the\n network interface is being managed by Amazon Web Services.

    \n
  • \n
  • \n

    \n network-interface.status - The status of the network interface\n (available) | in-use).

    \n
  • \n
  • \n

    \n network-interface.source-dest-check - Whether the network\n interface performs source/destination checking. A value of true\n means that checking is enabled, and false means that checking is\n disabled. The value must be false for the network interface to\n perform network address translation (NAT) in your VPC.

    \n
  • \n
  • \n

    \n network-interface.subnet-id - The ID of the subnet for the\n network interface.

    \n
  • \n
  • \n

    \n network-interface.vpc-id - The ID of the VPC for the network\n interface.

    \n
  • \n
  • \n

    \n outpost-arn - The Amazon Resource Name (ARN) of the\n Outpost.

    \n
  • \n
  • \n

    \n owner-id - The Amazon Web Services account ID of the instance\n owner.

    \n
  • \n
  • \n

    \n placement-group-name - The name of the placement group for the\n instance.

    \n
  • \n
  • \n

    \n placement-partition-number - The partition in which the instance is\n located.

    \n
  • \n
  • \n

    \n platform - The platform. To list only Windows instances, use\n windows.

    \n
  • \n
  • \n

    \n private-dns-name - The private IPv4 DNS name of the\n instance.

    \n
  • \n
  • \n

    \n private-ip-address - The private IPv4 address of the\n instance.

    \n
  • \n
  • \n

    \n product-code - The product code associated with the AMI used to\n launch the instance.

    \n
  • \n
  • \n

    \n product-code.type - The type of product code (devpay |\n marketplace).

    \n
  • \n
  • \n

    \n ramdisk-id - The RAM disk ID.

    \n
  • \n
  • \n

    \n reason - The reason for the current state of the instance (for\n example, shows \"User Initiated [date]\" when you stop or terminate the instance).\n Similar to the state-reason-code filter.

    \n
  • \n
  • \n

    \n requester-id - The ID of the entity that launched the instance on\n your behalf (for example, Amazon Web Services Management Console, Auto Scaling, and so\n on).

    \n
  • \n
  • \n

    \n reservation-id - The ID of the instance's reservation. A\n reservation ID is created any time you launch an instance. A reservation ID has\n a one-to-one relationship with an instance launch request, but can be associated\n with more than one instance if you launch multiple instances using the same\n launch request. For example, if you launch one instance, you get one reservation\n ID. If you launch ten instances using the same launch request, you also get one\n reservation ID.

    \n
  • \n
  • \n

    \n root-device-name - The device name of the root device volume (for\n example, /dev/sda1).

    \n
  • \n
  • \n

    \n root-device-type - The type of the root device volume\n (ebs | instance-store).

    \n
  • \n
  • \n

    \n source-dest-check - Indicates whether the instance performs\n source/destination checking. A value of true means that checking is\n enabled, and false means that checking is disabled. The value must\n be false for the instance to perform network address translation\n (NAT) in your VPC.

    \n
  • \n
  • \n

    \n spot-instance-request-id - The ID of the Spot Instance\n request.

    \n
  • \n
  • \n

    \n state-reason-code - The reason code for the state change.

    \n
  • \n
  • \n

    \n state-reason-message - A message that describes the state\n change.

    \n
  • \n
  • \n

    \n subnet-id - The ID of the subnet for the instance.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources that have a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n tenancy - The tenancy of an instance (dedicated |\n default | host).

    \n
  • \n
  • \n

    \n virtualization-type - The virtualization type of the instance\n (paravirtual | hvm).

    \n
  • \n
  • \n

    \n vpc-id - The ID of the VPC that the instance is running in.

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n affinity - The affinity setting for an instance running on a\n Dedicated Host (default | host).

    \n
  • \n
  • \n

    \n architecture - The instance architecture (i386 |\n x86_64 | arm64).

    \n
  • \n
  • \n

    \n availability-zone - The Availability Zone of the instance.

    \n
  • \n
  • \n

    \n block-device-mapping.attach-time - The attach time for an EBS\n volume mapped to the instance, for example,\n 2010-09-15T17:15:20.000Z.

    \n
  • \n
  • \n

    \n block-device-mapping.delete-on-termination - A Boolean that\n indicates whether the EBS volume is deleted on instance termination.

    \n
  • \n
  • \n

    \n block-device-mapping.device-name - The device name specified in the\n block device mapping (for example, /dev/sdh or\n xvdh).

    \n
  • \n
  • \n

    \n block-device-mapping.status - The status for the EBS volume\n (attaching | attached | detaching |\n detached).

    \n
  • \n
  • \n

    \n block-device-mapping.volume-id - The volume ID of the EBS\n volume.

    \n
  • \n
  • \n

    \n capacity-reservation-id - The ID of the Capacity Reservation into which the\n instance was launched.

    \n
  • \n
  • \n

    \n client-token - The idempotency token you provided when you launched\n the instance.

    \n
  • \n
  • \n

    \n dns-name - The public DNS name of the instance.

    \n
  • \n
  • \n

    \n hibernation-options.configured - A Boolean that indicates whether\n the instance is enabled for hibernation. A value of true means that\n the instance is enabled for hibernation.

    \n
  • \n
  • \n

    \n host-id - The ID of the Dedicated Host on which the instance is\n running, if applicable.

    \n
  • \n
  • \n

    \n hypervisor - The hypervisor type of the instance\n (ovm | xen). The value xen is used\n for both Xen and Nitro hypervisors.

    \n
  • \n
  • \n

    \n iam-instance-profile.arn - The instance profile associated with\n the instance. Specified as an ARN.

    \n
  • \n
  • \n

    \n image-id - The ID of the image used to launch the\n instance.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance.

    \n
  • \n
  • \n

    \n instance-lifecycle - Indicates whether this is a Spot Instance or\n a Scheduled Instance (spot | scheduled).

    \n
  • \n
  • \n

    \n instance-state-code - The state of the instance, as a 16-bit\n unsigned integer. The high byte is used for internal purposes and should be\n ignored. The low byte is set based on the state represented. The valid values\n are: 0 (pending), 16 (running), 32 (shutting-down), 48 (terminated), 64\n (stopping), and 80 (stopped).

    \n
  • \n
  • \n

    \n instance-state-name - The state of the instance\n (pending | running | shutting-down |\n terminated | stopping |\n stopped).

    \n
  • \n
  • \n

    \n instance-type - The type of instance (for example,\n t2.micro).

    \n
  • \n
  • \n

    \n instance.group-id - The ID of the security group for the\n instance.

    \n
  • \n
  • \n

    \n instance.group-name - The name of the security group for the\n instance.

    \n
  • \n
  • \n

    \n ip-address - The public IPv4 address of the instance.

    \n
  • \n
  • \n

    \n kernel-id - The kernel ID.

    \n
  • \n
  • \n

    \n key-name - The name of the key pair used when the instance was\n launched.

    \n
  • \n
  • \n

    \n launch-index - When launching multiple instances, this is the\n index for the instance in the launch group (for example, 0, 1, 2, and so on).\n

    \n
  • \n
  • \n

    \n launch-time - The time when the instance was launched, in the ISO\n 8601 format in the UTC time zone (YYYY-MM-DDThh:mm:ss.sssZ), for example,\n 2021-09-29T11:04:43.305Z. You can use a wildcard\n (*), for example, 2021-09-29T*, which matches an\n entire day.

    \n
  • \n
  • \n

    \n metadata-options.http-tokens - The metadata request authorization\n state (optional | required)

    \n
  • \n
  • \n

    \n metadata-options.http-put-response-hop-limit - The HTTP metadata\n request put response hop limit (integer, possible values 1 to\n 64)

    \n
  • \n
  • \n

    \n metadata-options.http-endpoint - The status of access to the HTTP\n metadata endpoint on your instance (enabled |\n disabled)

    \n
  • \n
  • \n

    \n metadata-options.instance-metadata-tags - The status of access to\n instance tags from the instance metadata (enabled |\n disabled)

    \n
  • \n
  • \n

    \n monitoring-state - Indicates whether detailed monitoring is\n enabled (disabled | enabled).

    \n
  • \n
  • \n

    \n network-interface.addresses.private-ip-address - The private IPv4\n address associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.addresses.primary - Specifies whether the IPv4\n address of the network interface is the primary private IPv4 address.

    \n
  • \n
  • \n

    \n network-interface.addresses.association.public-ip - The ID of the\n association of an Elastic IP address (IPv4) with a network interface.

    \n
  • \n
  • \n

    \n network-interface.addresses.association.ip-owner-id - The owner\n ID of the private IPv4 address associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.public-ip - The address of the\n Elastic IP address (IPv4) bound to the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.ip-owner-id - The owner of the\n Elastic IP address (IPv4) associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.allocation-id - The allocation ID\n returned when you allocated the Elastic IP address (IPv4) for your network\n interface.

    \n
  • \n
  • \n

    \n network-interface.association.association-id - The association ID\n returned when the network interface was associated with an IPv4 address.

    \n
  • \n
  • \n

    \n network-interface.attachment.attachment-id - The ID of the\n interface attachment.

    \n
  • \n
  • \n

    \n network-interface.attachment.instance-id - The ID of the instance\n to which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.instance-owner-id - The owner ID of\n the instance to which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.device-index - The device index to\n which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.status - The status of the\n attachment (attaching | attached |\n detaching | detached).

    \n
  • \n
  • \n

    \n network-interface.attachment.attach-time - The time that the\n network interface was attached to an instance.

    \n
  • \n
  • \n

    \n network-interface.attachment.delete-on-termination - Specifies\n whether the attachment is deleted when an instance is terminated.

    \n
  • \n
  • \n

    \n network-interface.availability-zone - The Availability Zone for\n the network interface.

    \n
  • \n
  • \n

    \n network-interface.description - The description of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.group-id - The ID of a security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.group-name - The name of a security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.ipv6-addresses.ipv6-address - The IPv6 address\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.mac-address - The MAC address of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.network-interface-id - The ID of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.owner-id - The ID of the owner of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.private-dns-name - The private DNS name of the\n network interface.

    \n
  • \n
  • \n

    \n network-interface.requester-id - The requester ID for the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.requester-managed - Indicates whether the\n network interface is being managed by Amazon Web Services.

    \n
  • \n
  • \n

    \n network-interface.status - The status of the network interface\n (available) | in-use).

    \n
  • \n
  • \n

    \n network-interface.source-dest-check - Whether the network\n interface performs source/destination checking. A value of true\n means that checking is enabled, and false means that checking is\n disabled. The value must be false for the network interface to\n perform network address translation (NAT) in your VPC.

    \n
  • \n
  • \n

    \n network-interface.subnet-id - The ID of the subnet for the\n network interface.

    \n
  • \n
  • \n

    \n network-interface.vpc-id - The ID of the VPC for the network\n interface.

    \n
  • \n
  • \n

    \n outpost-arn - The Amazon Resource Name (ARN) of the\n Outpost.

    \n
  • \n
  • \n

    \n owner-id - The Amazon Web Services account ID of the instance\n owner.

    \n
  • \n
  • \n

    \n placement-group-name - The name of the placement group for the\n instance.

    \n
  • \n
  • \n

    \n placement-partition-number - The partition in which the instance is\n located.

    \n
  • \n
  • \n

    \n platform - The platform. To list only Windows instances, use\n windows.

    \n
  • \n
  • \n

    \n private-dns-name - The private IPv4 DNS name of the\n instance.

    \n
  • \n
  • \n

    \n private-ip-address - The private IPv4 address of the\n instance.

    \n
  • \n
  • \n

    \n product-code - The product code associated with the AMI used to\n launch the instance.

    \n
  • \n
  • \n

    \n product-code.type - The type of product code (devpay |\n marketplace).

    \n
  • \n
  • \n

    \n ramdisk-id - The RAM disk ID.

    \n
  • \n
  • \n

    \n reason - The reason for the current state of the instance (for\n example, shows \"User Initiated [date]\" when you stop or terminate the instance).\n Similar to the state-reason-code filter.

    \n
  • \n
  • \n

    \n requester-id - The ID of the entity that launched the instance on\n your behalf (for example, Amazon Web Services Management Console, Auto Scaling, and so\n on).

    \n
  • \n
  • \n

    \n reservation-id - The ID of the instance's reservation. A\n reservation ID is created any time you launch an instance. A reservation ID has\n a one-to-one relationship with an instance launch request, but can be associated\n with more than one instance if you launch multiple instances using the same\n launch request. For example, if you launch one instance, you get one reservation\n ID. If you launch ten instances using the same launch request, you also get one\n reservation ID.

    \n
  • \n
  • \n

    \n root-device-name - The device name of the root device volume (for\n example, /dev/sda1).

    \n
  • \n
  • \n

    \n root-device-type - The type of the root device volume\n (ebs | instance-store).

    \n
  • \n
  • \n

    \n source-dest-check - Indicates whether the instance performs\n source/destination checking. A value of true means that checking is\n enabled, and false means that checking is disabled. The value must\n be false for the instance to perform network address translation\n (NAT) in your VPC.

    \n
  • \n
  • \n

    \n spot-instance-request-id - The ID of the Spot Instance\n request.

    \n
  • \n
  • \n

    \n state-reason-code - The reason code for the state change.

    \n
  • \n
  • \n

    \n state-reason-message - A message that describes the state\n change.

    \n
  • \n
  • \n

    \n subnet-id - The ID of the subnet for the instance.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources that have a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n tenancy - The tenancy of an instance (dedicated |\n default | host).

    \n
  • \n
  • \n

    \n virtualization-type - The virtualization type of the instance\n (paravirtual | hvm).

    \n
  • \n
  • \n

    \n vpc-id - The ID of the VPC that the instance is running in.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -29610,6 +30647,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInternetGateways": { @@ -29732,6 +30772,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpamPools": { @@ -29815,6 +30858,9 @@ "smithy.api#xmlName": "ipamPoolSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpamResourceDiscoveries": { @@ -29898,6 +30944,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpamResourceDiscoveryAssociations": { @@ -29981,6 +31030,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpamScopes": { @@ -30064,6 +31116,9 @@ "smithy.api#xmlName": "ipamScopeSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpams": { @@ -30147,6 +31202,9 @@ "smithy.api#xmlName": "ipamSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpv6Pools": { @@ -30230,6 +31288,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeKeyPairs": { @@ -30328,6 +31389,9 @@ "smithy.api#xmlName": "keySet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLaunchTemplateVersions": { @@ -30443,6 +31507,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLaunchTemplates": { @@ -30543,6 +31610,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations": { @@ -30626,6 +31696,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayRouteTableVpcAssociations": { @@ -30709,6 +31782,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayRouteTables": { @@ -30792,6 +31868,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayVirtualInterfaceGroups": { @@ -30875,6 +31954,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayVirtualInterfaces": { @@ -30958,6 +32040,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGateways": { @@ -31041,6 +32126,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeManagedPrefixLists": { @@ -31124,6 +32212,9 @@ "smithy.api#xmlName": "prefixListSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeMovingAddresses": { @@ -31135,7 +32226,7 @@ "target": "com.amazonaws.ec2#DescribeMovingAddressesResult" }, "traits": { - "smithy.api#documentation": "

Describes your Elastic IP addresses that are being moved to the EC2-VPC platform, or that are being restored to the EC2-Classic platform. This request does not return information about any other Elastic IP addresses in your account.

", + "smithy.api#documentation": "\n

This action is deprecated.

\n
\n

Describes your Elastic IP addresses that are being moved from or being restored to the EC2-Classic platform. \n This request does not return information about any other Elastic IP addresses in your account.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -31225,6 +32316,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNatGateways": { @@ -31394,6 +32488,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkAcls": { @@ -31489,6 +32586,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInsightsAccessScopeAnalyses": { @@ -31590,6 +32690,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInsightsAccessScopes": { @@ -31673,6 +32776,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInsightsAnalyses": { @@ -31774,6 +32880,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInsightsPaths": { @@ -31807,7 +32916,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters. The following are the possible values:

\n
    \n
  • \n

    destination - The ID of the resource.

    \n
  • \n
  • \n

    destination-port - The destination port.

    \n
  • \n
  • \n

    protocol - The protocol.

    \n
  • \n
  • \n

    source - The ID of the resource.

    \n
  • \n
", + "smithy.api#documentation": "

The filters. The following are the possible values:

\n
    \n
  • \n

    destination - The ID of the resource.

    \n
  • \n
  • \n

    filter-at-source.source-address - The source IPv4 address at the source.

    \n
  • \n
  • \n

    filter-at-source.source-port-range - The source port range at the source.

    \n
  • \n
  • \n

    filter-at-source.destination-address - The destination IPv4 address at the source.

    \n
  • \n
  • \n

    filter-at-source.destination-port-range - The destination port range at the source.

    \n
  • \n
  • \n

    filter-at-destination.source-address - The source IPv4 address at the destination.

    \n
  • \n
  • \n

    filter-at-destination.source-port-range - The source port range at the destination.

    \n
  • \n
  • \n

    filter-at-destination.destination-address - The destination IPv4 address at the destination.

    \n
  • \n
  • \n

    filter-at-destination.destination-port-range - The destination port range at the destination.

    \n
  • \n
  • \n

    protocol - The protocol.

    \n
  • \n
  • \n

    source - The ID of the resource.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -31857,6 +32966,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInterfaceAttribute": { @@ -31953,7 +33065,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeNetworkInterfaceAttribute.

" + "smithy.api#documentation": "

Contains the output of DescribeNetworkInterfaceAttribute.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInterfacePermissions": { @@ -32042,7 +33155,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output for DescribeNetworkInterfacePermissions.

" + "smithy.api#documentation": "

Contains the output for DescribeNetworkInterfacePermissions.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInterfaces": { @@ -32166,6 +33280,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribePlacementGroups": { @@ -32231,6 +33348,9 @@ "smithy.api#xmlName": "placementGroupSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribePrefixLists": { @@ -32314,6 +33434,9 @@ "smithy.api#xmlName": "prefixListSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribePrincipalIdFormat": { @@ -32400,6 +33523,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribePublicIpv4Pools": { @@ -32475,6 +33601,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeRegions": { @@ -32540,6 +33669,9 @@ "smithy.api#xmlName": "regionInfo" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReplaceRootVolumeTasks": { @@ -32633,6 +33765,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReservedInstances": { @@ -32704,7 +33839,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesListings.

" + "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesListings.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReservedInstancesModifications": { @@ -32730,7 +33866,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n client-token - The idempotency token for the modification request.

    \n
  • \n
  • \n

    \n create-date - The time when the modification request was created.

    \n
  • \n
  • \n

    \n effective-date - The time when the modification becomes effective.

    \n
  • \n
  • \n

    \n modification-result.reserved-instances-id - The ID for the Reserved Instances created as part of the modification request. This ID is only available when the status of the modification is fulfilled.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.availability-zone - The Availability Zone for the new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.instance-count - The number of new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.instance-type - The instance type of the new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.platform - The network platform of the new Reserved Instances (EC2-Classic | EC2-VPC).

    \n
  • \n
  • \n

    \n reserved-instances-id - The ID of the Reserved Instances modified.

    \n
  • \n
  • \n

    \n reserved-instances-modification-id - The ID of the modification request.

    \n
  • \n
  • \n

    \n status - The status of the Reserved Instances modification request\n (processing | fulfilled | failed).

    \n
  • \n
  • \n

    \n status-message - The reason for the status.

    \n
  • \n
  • \n

    \n update-date - The time when the modification request was last updated.

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n client-token - The idempotency token for the modification request.

    \n
  • \n
  • \n

    \n create-date - The time when the modification request was created.

    \n
  • \n
  • \n

    \n effective-date - The time when the modification becomes effective.

    \n
  • \n
  • \n

    \n modification-result.reserved-instances-id - The ID for the Reserved Instances created as part of the modification request. This ID is only available when the status of the modification is fulfilled.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.availability-zone - The Availability Zone for the new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.instance-count - The number of new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.instance-type - The instance type of the new Reserved Instances.

    \n
  • \n
  • \n

    \n reserved-instances-id - The ID of the Reserved Instances modified.

    \n
  • \n
  • \n

    \n reserved-instances-modification-id - The ID of the modification request.

    \n
  • \n
  • \n

    \n status - The status of the Reserved Instances modification request\n (processing | fulfilled | failed).

    \n
  • \n
  • \n

    \n status-message - The reason for the status.

    \n
  • \n
  • \n

    \n update-date - The time when the modification request was last updated.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -32776,7 +33912,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesModifications.

" + "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesModifications.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReservedInstancesOfferings": { @@ -32809,7 +33946,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone where the Reserved Instance can be\n used.

    \n
  • \n
  • \n

    \n duration - The duration of the Reserved Instance (for example, one year or\n three years), in seconds (31536000 | 94608000).

    \n
  • \n
  • \n

    \n fixed-price - The purchase price of the Reserved Instance (for example,\n 9800.0).

    \n
  • \n
  • \n

    \n instance-type - The instance type that is covered by the\n reservation.

    \n
  • \n
  • \n

    \n marketplace - Set to true to show only Reserved Instance\n Marketplace offerings. When this filter is not used, which is the default behavior, all\n offerings from both Amazon Web Services and the Reserved Instance Marketplace are listed.

    \n
  • \n
  • \n

    \n product-description - The Reserved Instance product platform description.\n Instances that include (Amazon VPC) in the product platform description will\n only be displayed to EC2-Classic account holders and are for use with Amazon VPC.\n (Linux/UNIX | Linux/UNIX (Amazon VPC) | SUSE\n Linux | SUSE Linux (Amazon VPC) | Red Hat Enterprise\n Linux | Red Hat Enterprise Linux (Amazon VPC) | Red Hat\n Enterprise Linux with HA (Amazon VPC) | Windows | Windows\n (Amazon VPC) | Windows with SQL Server Standard | Windows with\n SQL Server Standard (Amazon VPC) | Windows with SQL Server Web |\n Windows with SQL Server Web (Amazon VPC) | Windows with SQL Server\n Enterprise | Windows with SQL Server Enterprise (Amazon VPC))

    \n
  • \n
  • \n

    \n reserved-instances-offering-id - The Reserved Instances offering\n ID.

    \n
  • \n
  • \n

    \n scope - The scope of the Reserved Instance (Availability Zone or\n Region).

    \n
  • \n
  • \n

    \n usage-price - The usage price of the Reserved Instance, per hour (for\n example, 0.84).

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone where the Reserved Instance can be\n used.

    \n
  • \n
  • \n

    \n duration - The duration of the Reserved Instance (for example, one year or\n three years), in seconds (31536000 | 94608000).

    \n
  • \n
  • \n

    \n fixed-price - The purchase price of the Reserved Instance (for example,\n 9800.0).

    \n
  • \n
  • \n

    \n instance-type - The instance type that is covered by the\n reservation.

    \n
  • \n
  • \n

    \n marketplace - Set to true to show only Reserved Instance\n Marketplace offerings. When this filter is not used, which is the default behavior, all\n offerings from both Amazon Web Services and the Reserved Instance Marketplace are listed.

    \n
  • \n
  • \n

    \n product-description - The Reserved Instance product platform description\n (Linux/UNIX | Linux with SQL Server Standard |\n Linux with SQL Server Web | Linux with SQL Server Enterprise |\n SUSE Linux | \n Red Hat Enterprise Linux | Red Hat Enterprise Linux with HA | \n Windows | Windows with SQL Server Standard |\n Windows with SQL Server Web | Windows with SQL Server Enterprise).

    \n
  • \n
  • \n

    \n reserved-instances-offering-id - The Reserved Instances offering\n ID.

    \n
  • \n
  • \n

    \n scope - The scope of the Reserved Instance (Availability Zone or\n Region).

    \n
  • \n
  • \n

    \n usage-price - The usage price of the Reserved Instance, per hour (for\n example, 0.84).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -32941,7 +34078,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesOfferings.

" + "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesOfferings.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReservedInstancesRequest": { @@ -32950,7 +34088,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone where the Reserved Instance can be used.

    \n
  • \n
  • \n

    \n duration - The duration of the Reserved Instance (one year or three years), in seconds (31536000 | 94608000).

    \n
  • \n
  • \n

    \n end - The time when the Reserved Instance expires (for example, 2015-08-07T11:54:42.000Z).

    \n
  • \n
  • \n

    \n fixed-price - The purchase price of the Reserved Instance (for example, 9800.0).

    \n
  • \n
  • \n

    \n instance-type - The instance type that is covered by the reservation.

    \n
  • \n
  • \n

    \n scope - The scope of the Reserved Instance (Region or Availability Zone).

    \n
  • \n
  • \n

    \n product-description - The Reserved Instance product platform\n description. Instances that include (Amazon VPC) in the product platform\n description will only be displayed to EC2-Classic account holders and are for use with\n Amazon VPC (Linux/UNIX | Linux/UNIX (Amazon VPC) | SUSE\n Linux | SUSE Linux (Amazon VPC) | Red Hat Enterprise\n Linux | Red Hat Enterprise Linux (Amazon VPC) | Red Hat\n Enterprise Linux with HA (Amazon VPC) | Windows | Windows\n (Amazon VPC) | Windows with SQL Server Standard | Windows with\n SQL Server Standard (Amazon VPC) | Windows with SQL Server Web |\n Windows with SQL Server Web (Amazon VPC) | Windows with SQL Server\n Enterprise | Windows with SQL Server Enterprise (Amazon\n VPC)).

    \n
  • \n
  • \n

    \n reserved-instances-id - The ID of the Reserved Instance.

    \n
  • \n
  • \n

    \n start - The time at which the Reserved Instance purchase request was placed (for example, 2014-08-07T11:54:42.000Z).

    \n
  • \n
  • \n

    \n state - The state of the Reserved Instance (payment-pending | active | payment-failed | retired).

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n usage-price - The usage price of the Reserved Instance, per hour (for example, 0.84).

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone where the Reserved Instance can be used.

    \n
  • \n
  • \n

    \n duration - The duration of the Reserved Instance (one year or three years), in seconds (31536000 | 94608000).

    \n
  • \n
  • \n

    \n end - The time when the Reserved Instance expires (for example, 2015-08-07T11:54:42.000Z).

    \n
  • \n
  • \n

    \n fixed-price - The purchase price of the Reserved Instance (for example, 9800.0).

    \n
  • \n
  • \n

    \n instance-type - The instance type that is covered by the reservation.

    \n
  • \n
  • \n

    \n scope - The scope of the Reserved Instance (Region or Availability Zone).

    \n
  • \n
  • \n

    \n product-description - The Reserved Instance product platform description\n (Linux/UNIX | Linux with SQL Server Standard |\n Linux with SQL Server Web | Linux with SQL Server Enterprise |\n SUSE Linux | \n Red Hat Enterprise Linux | Red Hat Enterprise Linux with HA | \n Windows | Windows with SQL Server Standard |\n Windows with SQL Server Web | Windows with SQL Server Enterprise).

    \n
  • \n
  • \n

    \n reserved-instances-id - The ID of the Reserved Instance.

    \n
  • \n
  • \n

    \n start - The time at which the Reserved Instance purchase request was placed (for example, 2014-08-07T11:54:42.000Z).

    \n
  • \n
  • \n

    \n state - The state of the Reserved Instance (payment-pending | active | payment-failed | retired).

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n usage-price - The usage price of the Reserved Instance, per hour (for example, 0.84).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -33004,7 +34142,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output for DescribeReservedInstances.

" + "smithy.api#documentation": "

Contains the output for DescribeReservedInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeRouteTables": { @@ -33102,7 +34241,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeRouteTables.

" + "smithy.api#documentation": "

Contains the output of DescribeRouteTables.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeScheduledInstanceAvailability": { @@ -33147,7 +34287,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone (for example, us-west-2a).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example, c4.large).

    \n
  • \n
  • \n

    \n network-platform - The network platform (EC2-Classic or EC2-VPC).

    \n
  • \n
  • \n

    \n platform - The platform (Linux/UNIX or Windows).

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone (for example, us-west-2a).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example, c4.large).

    \n
  • \n
  • \n

    \n platform - The platform (Linux/UNIX or Windows).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -33224,7 +34364,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeScheduledInstanceAvailability.

" + "smithy.api#documentation": "

Contains the output of DescribeScheduledInstanceAvailability.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeScheduledInstances": { @@ -33259,7 +34400,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone (for example, us-west-2a).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example, c4.large).

    \n
  • \n
  • \n

    \n network-platform - The network platform (EC2-Classic or EC2-VPC).

    \n
  • \n
  • \n

    \n platform - The platform (Linux/UNIX or Windows).

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone (for example, us-west-2a).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example, c4.large).

    \n
  • \n
  • \n

    \n platform - The platform (Linux/UNIX or Windows).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -33317,7 +34458,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeScheduledInstances.

" + "smithy.api#documentation": "

Contains the output of DescribeScheduledInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSecurityGroupReferences": { @@ -33367,6 +34509,9 @@ "smithy.api#xmlName": "securityGroupReferenceSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSecurityGroupRules": { @@ -33460,6 +34605,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSecurityGroups": { @@ -33588,6 +34736,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSnapshotAttribute": { @@ -33663,6 +34814,9 @@ "smithy.api#xmlName": "snapshotId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSnapshotTierStatus": { @@ -33740,6 +34894,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSnapshots": { @@ -33866,6 +35023,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSpotDatafeedSubscription": { @@ -33912,7 +35072,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeSpotDatafeedSubscription.

" + "smithy.api#documentation": "

Contains the output of DescribeSpotDatafeedSubscription.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSpotFleetInstances": { @@ -34341,7 +35502,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone-group - The Availability Zone group.

    \n
  • \n
  • \n

    \n create-time - The time stamp when the Spot Instance request was\n created.

    \n
  • \n
  • \n

    \n fault-code - The fault code related to the request.

    \n
  • \n
  • \n

    \n fault-message - The fault message related to the request.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance that fulfilled the\n request.

    \n
  • \n
  • \n

    \n launch-group - The Spot Instance launch group.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.delete-on-termination - Indicates\n whether the EBS volume is deleted on instance termination.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.device-name - The device name for the\n volume in the block device mapping (for example, /dev/sdh or\n xvdh).

    \n
  • \n
  • \n

    \n launch.block-device-mapping.snapshot-id - The ID of the snapshot\n for the EBS volume.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.volume-size - The size of the EBS\n volume, in GiB.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.volume-type - The type of EBS volume:\n gp2 for General Purpose SSD, io1 or\n io2 for Provisioned IOPS SSD, st1 for Throughput\n Optimized HDD, sc1for Cold HDD, or standard for\n Magnetic.

    \n
  • \n
  • \n

    \n launch.group-id - The ID of the security group for the\n instance.

    \n
  • \n
  • \n

    \n launch.group-name - The name of the security group for the\n instance.

    \n
  • \n
  • \n

    \n launch.image-id - The ID of the AMI.

    \n
  • \n
  • \n

    \n launch.instance-type - The type of instance (for example,\n m3.medium).

    \n
  • \n
  • \n

    \n launch.kernel-id - The kernel ID.

    \n
  • \n
  • \n

    \n launch.key-name - The name of the key pair the instance launched\n with.

    \n
  • \n
  • \n

    \n launch.monitoring-enabled - Whether detailed monitoring is\n enabled for the Spot Instance.

    \n
  • \n
  • \n

    \n launch.ramdisk-id - The RAM disk ID.

    \n
  • \n
  • \n

    \n launched-availability-zone - The Availability Zone in which the\n request is launched.

    \n
  • \n
  • \n

    \n network-interface.addresses.primary - Indicates whether the IP\n address is the primary private IP address.

    \n
  • \n
  • \n

    \n network-interface.delete-on-termination - Indicates whether the\n network interface is deleted when the instance is terminated.

    \n
  • \n
  • \n

    \n network-interface.description - A description of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.device-index - The index of the device for the\n network interface attachment on the instance.

    \n
  • \n
  • \n

    \n network-interface.group-id - The ID of the security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.network-interface-id - The ID of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.private-ip-address - The primary private IP\n address of the network interface.

    \n
  • \n
  • \n

    \n network-interface.subnet-id - The ID of the subnet for the\n instance.

    \n
  • \n
  • \n

    \n product-description - The product description associated with the\n instance (Linux/UNIX | Windows).

    \n
  • \n
  • \n

    \n spot-instance-request-id - The Spot Instance request ID.

    \n
  • \n
  • \n

    \n spot-price - The maximum hourly price for any Spot Instance\n launched to fulfill the request.

    \n
  • \n
  • \n

    \n state - The state of the Spot Instance request (open\n | active | closed | cancelled |\n failed). Spot request status information can help you track\n your Amazon EC2 Spot Instance requests. For more information, see Spot\n request status in the Amazon EC2 User Guide for Linux Instances.

    \n
  • \n
  • \n

    \n status-code - The short code describing the most recent\n evaluation of your Spot Instance request.

    \n
  • \n
  • \n

    \n status-message - The message explaining the status of the Spot\n Instance request.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n type - The type of Spot Instance request (one-time |\n persistent).

    \n
  • \n
  • \n

    \n valid-from - The start date of the request.

    \n
  • \n
  • \n

    \n valid-until - The end date of the request.

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone-group - The Availability Zone group.

    \n
  • \n
  • \n

    \n create-time - The time stamp when the Spot Instance request was\n created.

    \n
  • \n
  • \n

    \n fault-code - The fault code related to the request.

    \n
  • \n
  • \n

    \n fault-message - The fault message related to the request.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance that fulfilled the\n request.

    \n
  • \n
  • \n

    \n launch-group - The Spot Instance launch group.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.delete-on-termination - Indicates\n whether the EBS volume is deleted on instance termination.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.device-name - The device name for the\n volume in the block device mapping (for example, /dev/sdh or\n xvdh).

    \n
  • \n
  • \n

    \n launch.block-device-mapping.snapshot-id - The ID of the snapshot\n for the EBS volume.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.volume-size - The size of the EBS\n volume, in GiB.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.volume-type - The type of EBS volume:\n gp2 for General Purpose SSD, io1 or\n io2 for Provisioned IOPS SSD, st1 for Throughput\n Optimized HDD, sc1for Cold HDD, or standard for\n Magnetic.

    \n
  • \n
  • \n

    \n launch.group-id - The ID of the security group for the\n instance.

    \n
  • \n
  • \n

    \n launch.group-name - The name of the security group for the\n instance.

    \n
  • \n
  • \n

    \n launch.image-id - The ID of the AMI.

    \n
  • \n
  • \n

    \n launch.instance-type - The type of instance (for example,\n m3.medium).

    \n
  • \n
  • \n

    \n launch.kernel-id - The kernel ID.

    \n
  • \n
  • \n

    \n launch.key-name - The name of the key pair the instance launched\n with.

    \n
  • \n
  • \n

    \n launch.monitoring-enabled - Whether detailed monitoring is\n enabled for the Spot Instance.

    \n
  • \n
  • \n

    \n launch.ramdisk-id - The RAM disk ID.

    \n
  • \n
  • \n

    \n launched-availability-zone - The Availability Zone in which the\n request is launched.

    \n
  • \n
  • \n

    \n network-interface.addresses.primary - Indicates whether the IP\n address is the primary private IP address.

    \n
  • \n
  • \n

    \n network-interface.delete-on-termination - Indicates whether the\n network interface is deleted when the instance is terminated.

    \n
  • \n
  • \n

    \n network-interface.description - A description of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.device-index - The index of the device for the\n network interface attachment on the instance.

    \n
  • \n
  • \n

    \n network-interface.group-id - The ID of the security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.network-interface-id - The ID of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.private-ip-address - The primary private IP\n address of the network interface.

    \n
  • \n
  • \n

    \n network-interface.subnet-id - The ID of the subnet for the\n instance.

    \n
  • \n
  • \n

    \n product-description - The product description associated with the\n instance (Linux/UNIX | Windows).

    \n
  • \n
  • \n

    \n spot-instance-request-id - The Spot Instance request ID.

    \n
  • \n
  • \n

    \n spot-price - The maximum hourly price for any Spot Instance\n launched to fulfill the request.

    \n
  • \n
  • \n

    \n state - The state of the Spot Instance request (open\n | active | closed | cancelled |\n failed). Spot request status information can help you track\n your Amazon EC2 Spot Instance requests. For more information, see Spot\n request status in the Amazon EC2 User Guide for Linux Instances.

    \n
  • \n
  • \n

    \n status-code - The short code describing the most recent\n evaluation of your Spot Instance request.

    \n
  • \n
  • \n

    \n status-message - The message explaining the status of the Spot\n Instance request.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n type - The type of Spot Instance request (one-time |\n persistent).

    \n
  • \n
  • \n

    \n valid-from - The start date of the request.

    \n
  • \n
  • \n

    \n valid-until - The end date of the request.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -34358,7 +35519,7 @@ "SpotInstanceRequestIds": { "target": "com.amazonaws.ec2#SpotInstanceRequestIdList", "traits": { - "smithy.api#documentation": "

One or more Spot Instance request IDs.

", + "smithy.api#documentation": "

The IDs of the Spot Instance requests.

", "smithy.api#xmlName": "SpotInstanceRequestId" } }, @@ -34389,7 +35550,7 @@ "target": "com.amazonaws.ec2#SpotInstanceRequestList", "traits": { "aws.protocols#ec2QueryName": "SpotInstanceRequestSet", - "smithy.api#documentation": "

One or more Spot Instance requests.

", + "smithy.api#documentation": "

The Spot Instance requests.

", "smithy.api#xmlName": "spotInstanceRequestSet" } }, @@ -34403,7 +35564,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeSpotInstanceRequests.

" + "smithy.api#documentation": "

Contains the output of DescribeSpotInstanceRequests.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSpotPriceHistory": { @@ -34430,7 +35592,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone for which prices should\n be returned.

    \n
  • \n
  • \n

    \n instance-type - The type of instance (for example,\n m3.medium).

    \n
  • \n
  • \n

    \n product-description - The product description for the Spot price\n (Linux/UNIX | Red Hat Enterprise Linux |\n SUSE Linux | Windows | Linux/UNIX (Amazon\n VPC) | Red Hat Enterprise Linux (Amazon VPC) |\n SUSE Linux (Amazon VPC) | Windows (Amazon\n VPC)).

    \n
  • \n
  • \n

    \n spot-price - The Spot price. The value must match exactly (or use\n wildcards; greater than or less than comparison is not supported).

    \n
  • \n
  • \n

    \n timestamp - The time stamp of the Spot price history, in UTC format\n (for example,\n YYYY-MM-DDTHH:MM:SSZ).\n You can use wildcards (* and ?). Greater than or less than comparison is not\n supported.

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone for which prices should\n be returned.

    \n
  • \n
  • \n

    \n instance-type - The type of instance (for example,\n m3.medium).

    \n
  • \n
  • \n

    \n product-description - The product description for the Spot price\n (Linux/UNIX | Red Hat Enterprise Linux |\n SUSE Linux | Windows | Linux/UNIX (Amazon\n VPC) | Red Hat Enterprise Linux (Amazon VPC) |\n SUSE Linux (Amazon VPC) | Windows (Amazon\n VPC)).

    \n
  • \n
  • \n

    \n spot-price - The Spot price. The value must match exactly (or use\n wildcards; greater than or less than comparison is not supported).

    \n
  • \n
  • \n

    \n timestamp - The time stamp of the Spot price history, in UTC format\n (for example,\n YYYY-MM-DDTHH:MM:SSZ).\n You can use wildcards (* and ?). Greater than or less than comparison is not\n supported.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -34527,7 +35689,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeSpotPriceHistory.

" + "smithy.api#documentation": "

Contains the output of DescribeSpotPriceHistory.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeStaleSecurityGroups": { @@ -34624,6 +35787,9 @@ "smithy.api#xmlName": "staleSecurityGroupSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeStoreImageTasks": { @@ -34717,6 +35883,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSubnets": { @@ -34829,6 +35998,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTags": { @@ -34911,6 +36083,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTrafficMirrorFilters": { @@ -34994,6 +36169,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTrafficMirrorSessions": { @@ -35077,6 +36255,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTrafficMirrorTargets": { @@ -35160,6 +36341,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayAttachments": { @@ -35242,6 +36426,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayConnectPeers": { @@ -35324,6 +36511,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayConnects": { @@ -35406,6 +36596,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayMulticastDomains": { @@ -35488,6 +36681,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayPeeringAttachments": { @@ -35570,6 +36766,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayPolicyTables": { @@ -35652,6 +36851,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayRouteTableAnnouncements": { @@ -35734,6 +36936,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayRouteTables": { @@ -35816,6 +37021,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayVpcAttachments": { @@ -35898,6 +37106,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGateways": { @@ -35980,6 +37191,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTrunkInterfaceAssociations": { @@ -36073,6 +37287,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessEndpoints": { @@ -36084,7 +37301,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessEndpointsResult" }, "traits": { - "smithy.api#documentation": "

Describe Amazon Web Services Verified Access endpoints.

", + "smithy.api#documentation": "

Describes the specified Amazon Web Services Verified Access endpoints.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36109,20 +37326,20 @@ "VerifiedAccessEndpointIds": { "target": "com.amazonaws.ec2#VerifiedAccessEndpointIdList", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#xmlName": "VerifiedAccessEndpointId" } }, "VerifiedAccessInstanceId": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

The ID of the Verified Access instance.

" } }, "VerifiedAccessGroupId": { "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

" + "smithy.api#documentation": "

The ID of the Verified Access group.

" } }, "MaxResults": { @@ -36166,7 +37383,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointList", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessEndpointSet", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#xmlName": "verifiedAccessEndpointSet" } }, @@ -36178,6 +37395,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessGroupMaxResults": { @@ -36199,7 +37419,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessGroupsResult" }, "traits": { - "smithy.api#documentation": "

Describe details of existing Verified Access groups.

", + "smithy.api#documentation": "

Describes the specified Verified Access groups.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36214,14 +37434,14 @@ "VerifiedAccessGroupIds": { "target": "com.amazonaws.ec2#VerifiedAccessGroupIdList", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access groups.

", + "smithy.api#documentation": "

The ID of the Verified Access groups.

", "smithy.api#xmlName": "VerifiedAccessGroupId" } }, "VerifiedAccessInstanceId": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

The ID of the Verified Access instance.

" } }, "MaxResults": { @@ -36277,6 +37497,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessInstanceLoggingConfigurations": { @@ -36288,7 +37511,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessInstanceLoggingConfigurationsResult" }, "traits": { - "smithy.api#documentation": "

Describes the current logging configuration for the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

Describes the specified Amazon Web Services Verified Access instances.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36313,7 +37536,7 @@ "VerifiedAccessInstanceIds": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceIdList", "traits": { - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The IDs of the Verified Access instances.

", "smithy.api#xmlName": "VerifiedAccessInstanceId" } }, @@ -36358,7 +37581,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceLoggingConfigurationList", "traits": { "aws.protocols#ec2QueryName": "LoggingConfigurationSet", - "smithy.api#documentation": "

The current logging configuration for the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The current logging configuration for the Verified Access instances.

", "smithy.api#xmlName": "loggingConfigurationSet" } }, @@ -36370,6 +37593,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessInstances": { @@ -36381,7 +37607,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessInstancesResult" }, "traits": { - "smithy.api#documentation": "

Describe Verified Access instances.

", + "smithy.api#documentation": "

Describes the specified Amazon Web Services Verified Access instances.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36406,7 +37632,7 @@ "VerifiedAccessInstanceIds": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceIdList", "traits": { - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The IDs of the Verified Access instances.

", "smithy.api#xmlName": "VerifiedAccessInstanceId" } }, @@ -36451,7 +37677,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceList", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstanceSet", - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The IDs of the Verified Access instances.

", "smithy.api#xmlName": "verifiedAccessInstanceSet" } }, @@ -36463,6 +37689,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessTrustProviders": { @@ -36474,7 +37703,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessTrustProvidersResult" }, "traits": { - "smithy.api#documentation": "

Describe details of existing Verified Access trust providers.

", + "smithy.api#documentation": "

Describes the specified Amazon Web Services Verified Access trust providers.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36499,7 +37728,7 @@ "VerifiedAccessTrustProviderIds": { "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderIdList", "traits": { - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access trust providers.

", + "smithy.api#documentation": "

The IDs of the Verified Access trust providers.

", "smithy.api#xmlName": "VerifiedAccessTrustProviderId" } }, @@ -36544,7 +37773,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderList", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProviderSet", - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access trust providers.

", + "smithy.api#documentation": "

The IDs of the Verified Access trust providers.

", "smithy.api#xmlName": "verifiedAccessTrustProviderSet" } }, @@ -36556,6 +37785,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVolumeAttribute": { @@ -36631,6 +37863,9 @@ "smithy.api#xmlName": "volumeId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVolumeStatus": { @@ -36716,6 +37951,9 @@ "smithy.api#xmlName": "volumeStatusSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVolumes": { @@ -36893,6 +38131,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVolumesRequest": { @@ -36964,6 +38205,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcAttribute": { @@ -37047,6 +38291,9 @@ "smithy.api#xmlName": "enableNetworkAddressUsageMetrics" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcClassicLink": { @@ -37150,6 +38397,9 @@ "smithy.api#xmlName": "vpcs" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcClassicLinkRequest": { @@ -37195,6 +38445,9 @@ "smithy.api#xmlName": "vpcSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointConnectionNotifications": { @@ -37277,6 +38530,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointConnections": { @@ -37353,6 +38609,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointServiceConfigurations": { @@ -37436,6 +38695,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointServicePermissions": { @@ -37520,6 +38782,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointServices": { @@ -37605,6 +38870,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpoints": { @@ -37688,6 +38956,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcPeeringConnections": { @@ -37826,6 +39097,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcs": { @@ -37958,6 +39232,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpnConnections": { @@ -38080,7 +39357,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeVpnConnections.

" + "smithy.api#documentation": "

Contains the output of DescribeVpnConnections.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpnGateways": { @@ -38141,7 +39419,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeVpnGateways.

" + "smithy.api#documentation": "

Contains the output of DescribeVpnGateways.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DestinationFileFormat": { @@ -38290,6 +39569,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DetachInternetGateway": { @@ -38402,7 +39684,7 @@ "target": "com.amazonaws.ec2#DetachVerifiedAccessTrustProviderResult" }, "traits": { - "smithy.api#documentation": "

Detach a trust provider from an Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

Detaches the specified Amazon Web Services Verified Access trust provider from the specified Amazon Web Services Verified Access instance.

" } }, "com.amazonaws.ec2#DetachVerifiedAccessTrustProviderRequest": { @@ -38412,7 +39694,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, @@ -38420,7 +39702,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#required": {} } }, @@ -38451,7 +39733,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } }, @@ -38459,10 +39741,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DetachVolume": { @@ -38583,7 +39868,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for an Amazon Web Services Verified Access device-identity based trust provider.

" + "smithy.api#documentation": "

Describes the options for an Amazon Web Services Verified Access device-identity based trust provider.

" } }, "com.amazonaws.ec2#DeviceTrustProviderType": { @@ -38800,6 +40085,9 @@ "smithy.api#xmlName": "addressTransfer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableAwsNetworkPerformanceMetricSubscription": { @@ -38867,6 +40155,9 @@ "smithy.api#xmlName": "output" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableEbsEncryptionByDefault": { @@ -38910,6 +40201,9 @@ "smithy.api#xmlName": "ebsEncryptionByDefault" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableFastLaunch": { @@ -39033,6 +40327,9 @@ "smithy.api#xmlName": "stateTransitionTime" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableFastSnapshotRestoreErrorItem": { @@ -39295,6 +40592,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableImageDeprecation": { @@ -39346,6 +40646,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableIpamOrganizationAdminAccount": { @@ -39397,6 +40700,9 @@ "smithy.api#xmlName": "success" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableSerialConsoleAccess": { @@ -39440,6 +40746,9 @@ "smithy.api#xmlName": "serialConsoleAccessEnabled" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableTransitGatewayRouteTablePropagation": { @@ -39501,6 +40810,9 @@ "smithy.api#xmlName": "propagation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableVgwRoutePropagation": { @@ -39600,6 +40912,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableVpcClassicLinkRequest": { @@ -39643,6 +40958,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateAddress": { @@ -39654,7 +40972,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Disassociates an Elastic IP address from the instance or network interface it's associated with.

\n

An Elastic IP address is for use in either the EC2-Classic platform or in a VPC. For more\n\t\t\tinformation, see Elastic IP\n\t\t\t\tAddresses in the Amazon Elastic Compute Cloud User Guide.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
\n

This is an idempotent operation. If you perform the operation more than once, Amazon EC2 doesn't return an error.

" + "smithy.api#documentation": "

Disassociates an Elastic IP address from the instance or network interface it's associated with.

\n

This is an idempotent operation. If you perform the operation more than once, Amazon EC2 doesn't return an error.

" } }, "com.amazonaws.ec2#DisassociateAddressRequest": { @@ -39663,13 +40981,13 @@ "AssociationId": { "target": "com.amazonaws.ec2#ElasticIpAssociationId", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The association ID. Required for EC2-VPC.

" + "smithy.api#documentation": "

The association ID. This parameter is required.

" } }, "PublicIp": { "target": "com.amazonaws.ec2#EipAllocationPublicIp", "traits": { - "smithy.api#documentation": "

[EC2-Classic] The Elastic IP address. Required for EC2-Classic.

" + "smithy.api#documentation": "

Deprecated.

" } }, "DryRun": { @@ -39750,6 +41068,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateEnclaveCertificateIamRole": { @@ -39809,6 +41130,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateIamInstanceProfile": { @@ -39850,6 +41174,9 @@ "smithy.api#xmlName": "iamInstanceProfileAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateInstanceEventWindow": { @@ -39907,6 +41234,9 @@ "smithy.api#xmlName": "instanceEventWindow" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateIpamResourceDiscovery": { @@ -39956,6 +41286,9 @@ "smithy.api#xmlName": "ipamResourceDiscoveryAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateNatGatewayAddress": { @@ -40030,6 +41363,9 @@ "smithy.api#xmlName": "natGatewayAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateRouteTable": { @@ -40121,6 +41457,9 @@ "smithy.api#xmlName": "subnetId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateTransitGatewayMulticastDomain": { @@ -40186,6 +41525,9 @@ "smithy.api#xmlName": "associations" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateTransitGatewayPolicyTable": { @@ -40243,6 +41585,9 @@ "smithy.api#xmlName": "association" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateTransitGatewayRouteTable": { @@ -40300,6 +41645,9 @@ "smithy.api#xmlName": "association" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateTrunkInterface": { @@ -40366,6 +41714,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateVpcCidrBlock": { @@ -40425,6 +41776,9 @@ "smithy.api#xmlName": "vpcId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DiskCount": { @@ -40476,7 +41830,7 @@ } }, "ImportManifestUrl": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#ImportManifestUrl", "traits": { "aws.protocols#ec2QueryName": "ImportManifestUrl", "smithy.api#documentation": "

A presigned URL for the import manifest stored in Amazon S3. For information about creating a presigned URL for\n an Amazon S3 object, read the \"Query String Request Authentication Alternative\" section of the Authenticating REST Requests topic in\n the Amazon Simple Storage Service Developer Guide.

\n

For information about the import manifest referenced by this API action, see VM Import Manifest.

", @@ -40523,7 +41877,7 @@ } }, "ImportManifestUrl": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#ImportManifestUrl", "traits": { "aws.protocols#ec2QueryName": "ImportManifestUrl", "smithy.api#clientOptional": {}, @@ -41201,6 +42555,177 @@ } } }, + "com.amazonaws.ec2#Ec2InstanceConnectEndpoint": { + "type": "structure", + "members": { + "OwnerId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "OwnerId", + "smithy.api#documentation": "

The ID of the Amazon Web Services account that created the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "ownerId" + } + }, + "InstanceConnectEndpointId": { + "target": "com.amazonaws.ec2#InstanceConnectEndpointId", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpointId", + "smithy.api#documentation": "

The ID of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "instanceConnectEndpointId" + } + }, + "InstanceConnectEndpointArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpointArn", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "instanceConnectEndpointArn" + } + }, + "State": { + "target": "com.amazonaws.ec2#Ec2InstanceConnectEndpointState", + "traits": { + "aws.protocols#ec2QueryName": "State", + "smithy.api#documentation": "

The current state of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "state" + } + }, + "StateMessage": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "StateMessage", + "smithy.api#documentation": "

The message for the current state of the EC2 Instance Connect Endpoint. \n Can include a failure message.

", + "smithy.api#xmlName": "stateMessage" + } + }, + "DnsName": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "DnsName", + "smithy.api#documentation": "

The DNS name of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "dnsName" + } + }, + "FipsDnsName": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "FipsDnsName", + "smithy.api#documentation": "

", + "smithy.api#xmlName": "fipsDnsName" + } + }, + "NetworkInterfaceIds": { + "target": "com.amazonaws.ec2#NetworkInterfaceIdSet", + "traits": { + "aws.protocols#ec2QueryName": "NetworkInterfaceIdSet", + "smithy.api#documentation": "

The ID of the elastic network interface that Amazon EC2 automatically created when creating the EC2\n Instance Connect Endpoint.

", + "smithy.api#xmlName": "networkInterfaceIdSet" + } + }, + "VpcId": { + "target": "com.amazonaws.ec2#VpcId", + "traits": { + "aws.protocols#ec2QueryName": "VpcId", + "smithy.api#documentation": "

The ID of the VPC in which the EC2 Instance Connect Endpoint was created.

", + "smithy.api#xmlName": "vpcId" + } + }, + "AvailabilityZone": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "AvailabilityZone", + "smithy.api#documentation": "

The Availability Zone of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "availabilityZone" + } + }, + "CreatedAt": { + "target": "com.amazonaws.ec2#MillisecondDateTime", + "traits": { + "aws.protocols#ec2QueryName": "CreatedAt", + "smithy.api#documentation": "

The date and time that the EC2 Instance Connect Endpoint was created.

", + "smithy.api#xmlName": "createdAt" + } + }, + "SubnetId": { + "target": "com.amazonaws.ec2#SubnetId", + "traits": { + "aws.protocols#ec2QueryName": "SubnetId", + "smithy.api#documentation": "

The ID of the subnet in which the EC2 Instance Connect Endpoint was created.

", + "smithy.api#xmlName": "subnetId" + } + }, + "PreserveClientIp": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "aws.protocols#ec2QueryName": "PreserveClientIp", + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Indicates whether your client's IP address is preserved as the source. The value is true or false.

\n
    \n
  • \n

    If true, your client's IP address is used when you connect to a resource.

    \n
  • \n
  • \n

    If false, the elastic network interface IP address is used when you connect to a resource.

    \n
  • \n
\n

Default: true\n

", + "smithy.api#xmlName": "preserveClientIp" + } + }, + "SecurityGroupIds": { + "target": "com.amazonaws.ec2#SecurityGroupIdSet", + "traits": { + "aws.protocols#ec2QueryName": "SecurityGroupIdSet", + "smithy.api#documentation": "

The security groups associated with the endpoint. If you didn't specify a security group, \n the default security group for your VPC is associated with the endpoint.

", + "smithy.api#xmlName": "securityGroupIdSet" + } + }, + "Tags": { + "target": "com.amazonaws.ec2#TagList", + "traits": { + "aws.protocols#ec2QueryName": "TagSet", + "smithy.api#documentation": "

The tags assigned to the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "tagSet" + } + } + }, + "traits": { + "smithy.api#documentation": "

The EC2 Instance Connect Endpoint.

" + } + }, + "com.amazonaws.ec2#Ec2InstanceConnectEndpointState": { + "type": "enum", + "members": { + "create_in_progress": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "create-in-progress" + } + }, + "create_complete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "create-complete" + } + }, + "create_failed": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "create-failed" + } + }, + "delete_in_progress": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "delete-in-progress" + } + }, + "delete_complete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "delete-complete" + } + }, + "delete_failed": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "delete-failed" + } + } + } + }, "com.amazonaws.ec2#EfaInfo": { "type": "structure", "members": { @@ -41735,6 +43260,9 @@ "smithy.api#xmlName": "addressTransfer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableAwsNetworkPerformanceMetricSubscription": { @@ -41802,6 +43330,9 @@ "smithy.api#xmlName": "output" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableEbsEncryptionByDefault": { @@ -41845,6 +43376,9 @@ "smithy.api#xmlName": "ebsEncryptionByDefault" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableFastLaunch": { @@ -41986,6 +43520,9 @@ "smithy.api#xmlName": "stateTransitionTime" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableFastSnapshotRestoreErrorItem": { @@ -42248,6 +43785,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableImageDeprecation": { @@ -42307,6 +43847,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableIpamOrganizationAdminAccount": { @@ -42358,6 +43901,9 @@ "smithy.api#xmlName": "success" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableReachabilityAnalyzerOrganizationSharing": { @@ -42401,6 +43947,9 @@ "smithy.api#xmlName": "returnValue" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableSerialConsoleAccess": { @@ -42444,6 +43993,9 @@ "smithy.api#xmlName": "serialConsoleAccessEnabled" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableTransitGatewayRouteTablePropagation": { @@ -42505,6 +44057,9 @@ "smithy.api#xmlName": "propagation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableVgwRoutePropagation": { @@ -42644,6 +44199,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableVpcClassicLinkRequest": { @@ -42687,6 +44245,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnclaveOptions": { @@ -43347,6 +44908,22 @@ "smithy.api#documentation": "

The Region for the component.

", "smithy.api#xmlName": "componentRegion" } + }, + "FirewallStatelessRule": { + "target": "com.amazonaws.ec2#FirewallStatelessRule", + "traits": { + "aws.protocols#ec2QueryName": "FirewallStatelessRule", + "smithy.api#documentation": "

The Network Firewall stateless rule.

", + "smithy.api#xmlName": "firewallStatelessRule" + } + }, + "FirewallStatefulRule": { + "target": "com.amazonaws.ec2#FirewallStatefulRule", + "traits": { + "aws.protocols#ec2QueryName": "FirewallStatefulRule", + "smithy.api#documentation": "

The Network Firewall stateful rule.

", + "smithy.api#xmlName": "firewallStatefulRule" + } } }, "traits": { @@ -43417,6 +44994,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ExportClientVpnClientConfiguration": { @@ -43466,6 +45046,9 @@ "smithy.api#xmlName": "clientConfiguration" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ExportEnvironment": { @@ -43652,6 +45235,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ExportImageTask": { @@ -44049,6 +45635,9 @@ "smithy.api#xmlName": "s3Location" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ExportVmTaskId": { @@ -44384,6 +45973,34 @@ } } }, + "com.amazonaws.ec2#FilterPortRange": { + "type": "structure", + "members": { + "FromPort": { + "target": "com.amazonaws.ec2#Port", + "traits": { + "aws.protocols#ec2QueryName": "FromPort", + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The first port in the range.

", + "smithy.api#xmlName": "fromPort" + } + }, + "ToPort": { + "target": "com.amazonaws.ec2#Port", + "traits": { + "aws.protocols#ec2QueryName": "ToPort", + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The last port in the range.

", + "smithy.api#xmlName": "toPort" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a port range.

" + } + }, "com.amazonaws.ec2#FindingsFound": { "type": "enum", "members": { @@ -44407,6 +46024,152 @@ } } }, + "com.amazonaws.ec2#FirewallStatefulRule": { + "type": "structure", + "members": { + "RuleGroupArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupArn", + "smithy.api#documentation": "

The ARN of the stateful rule group.

", + "smithy.api#xmlName": "ruleGroupArn" + } + }, + "Sources": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "aws.protocols#ec2QueryName": "SourceSet", + "smithy.api#documentation": "

The source IP addresses, in CIDR notation.

", + "smithy.api#xmlName": "sourceSet" + } + }, + "Destinations": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "aws.protocols#ec2QueryName": "DestinationSet", + "smithy.api#documentation": "

The destination IP addresses, in CIDR notation.

", + "smithy.api#xmlName": "destinationSet" + } + }, + "SourcePorts": { + "target": "com.amazonaws.ec2#PortRangeList", + "traits": { + "aws.protocols#ec2QueryName": "SourcePortSet", + "smithy.api#documentation": "

The source ports.

", + "smithy.api#xmlName": "sourcePortSet" + } + }, + "DestinationPorts": { + "target": "com.amazonaws.ec2#PortRangeList", + "traits": { + "aws.protocols#ec2QueryName": "DestinationPortSet", + "smithy.api#documentation": "

The destination ports.

", + "smithy.api#xmlName": "destinationPortSet" + } + }, + "Protocol": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "Protocol", + "smithy.api#documentation": "

The protocol.

", + "smithy.api#xmlName": "protocol" + } + }, + "RuleAction": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "RuleAction", + "smithy.api#documentation": "

The rule action. The possible values are pass, drop, and \n alert.

", + "smithy.api#xmlName": "ruleAction" + } + }, + "Direction": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "Direction", + "smithy.api#documentation": "

The direction. The possible values are FORWARD and ANY.

", + "smithy.api#xmlName": "direction" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a stateful rule.

" + } + }, + "com.amazonaws.ec2#FirewallStatelessRule": { + "type": "structure", + "members": { + "RuleGroupArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupArn", + "smithy.api#documentation": "

The ARN of the stateless rule group.

", + "smithy.api#xmlName": "ruleGroupArn" + } + }, + "Sources": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "aws.protocols#ec2QueryName": "SourceSet", + "smithy.api#documentation": "

The source IP addresses, in CIDR notation.

", + "smithy.api#xmlName": "sourceSet" + } + }, + "Destinations": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "aws.protocols#ec2QueryName": "DestinationSet", + "smithy.api#documentation": "

The destination IP addresses, in CIDR notation.

", + "smithy.api#xmlName": "destinationSet" + } + }, + "SourcePorts": { + "target": "com.amazonaws.ec2#PortRangeList", + "traits": { + "aws.protocols#ec2QueryName": "SourcePortSet", + "smithy.api#documentation": "

The source ports.

", + "smithy.api#xmlName": "sourcePortSet" + } + }, + "DestinationPorts": { + "target": "com.amazonaws.ec2#PortRangeList", + "traits": { + "aws.protocols#ec2QueryName": "DestinationPortSet", + "smithy.api#documentation": "

The destination ports.

", + "smithy.api#xmlName": "destinationPortSet" + } + }, + "Protocols": { + "target": "com.amazonaws.ec2#ProtocolIntList", + "traits": { + "aws.protocols#ec2QueryName": "ProtocolSet", + "smithy.api#documentation": "

The protocols.

", + "smithy.api#xmlName": "protocolSet" + } + }, + "RuleAction": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "RuleAction", + "smithy.api#documentation": "

The rule action. The possible values are pass, drop, and \n forward_to_site.

", + "smithy.api#xmlName": "ruleAction" + } + }, + "Priority": { + "target": "com.amazonaws.ec2#Priority", + "traits": { + "aws.protocols#ec2QueryName": "Priority", + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The rule priority.

", + "smithy.api#xmlName": "priority" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a stateless rule.

" + } + }, "com.amazonaws.ec2#FleetActivityStatus": { "type": "enum", "members": { @@ -45998,6 +47761,9 @@ "smithy.api#xmlName": "associatedRoleSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetAssociatedIpv6PoolCidrs": { @@ -46075,6 +47841,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetAwsNetworkPerformanceData": { @@ -46163,6 +47932,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetCapacityReservationUsage": { @@ -46288,6 +48060,9 @@ "smithy.api#xmlName": "instanceUsageSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetCoipPoolUsage": { @@ -46374,6 +48149,9 @@ "smithy.api#xmlName": "localGatewayRouteTableId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetConsoleOutput": { @@ -46449,6 +48227,9 @@ "smithy.api#xmlName": "timestamp" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetConsoleScreenshot": { @@ -46514,6 +48295,9 @@ "smithy.api#xmlName": "instanceId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetDefaultCreditSpecification": { @@ -46563,6 +48347,9 @@ "smithy.api#xmlName": "instanceFamilyCreditSpecification" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetEbsDefaultKmsKeyId": { @@ -46604,6 +48391,9 @@ "smithy.api#xmlName": "kmsKeyId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetEbsEncryptionByDefault": { @@ -46647,6 +48437,9 @@ "smithy.api#xmlName": "ebsEncryptionByDefault" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetFlowLogsIntegrationTemplate": { @@ -46713,6 +48506,9 @@ "smithy.api#xmlName": "result" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetGroupsForCapacityReservation": { @@ -46740,7 +48536,7 @@ "target": "com.amazonaws.ec2#CapacityReservationId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Capacity Reservation.

", + "smithy.api#documentation": "

The ID of the Capacity Reservation. If you specify a Capacity Reservation that is shared \n\t\t\twith you, the operation returns only Capacity Reservation groups that you own.

", "smithy.api#required": {} } }, @@ -46800,6 +48596,9 @@ "smithy.api#xmlName": "capacityReservationGroupSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetHostReservationPurchasePreview": { @@ -46873,6 +48672,9 @@ "smithy.api#xmlName": "totalUpfrontPrice" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetInstanceTypesFromInstanceRequirements": { @@ -46968,6 +48770,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetInstanceUefiData": { @@ -47026,6 +48831,9 @@ "smithy.api#xmlName": "uefiData" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamAddressHistory": { @@ -47129,6 +48937,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamDiscoveredAccounts": { @@ -47221,6 +49032,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamDiscoveredResourceCidrs": { @@ -47313,6 +49127,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamPoolAllocations": { @@ -47324,7 +49141,7 @@ "target": "com.amazonaws.ec2#GetIpamPoolAllocationsResult" }, "traits": { - "smithy.api#documentation": "

Get a list of all the CIDR allocations in an IPAM pool.

", + "smithy.api#documentation": "

Get a list of all the CIDR allocations in an IPAM pool. The Region you use should be the IPAM pool locale. The locale is the Amazon Web Services Region where this IPAM pool is available for allocations.

\n \n

If you use this action after AllocateIpamPoolCidr or ReleaseIpamPoolAllocation, note that all EC2 API actions follow an eventual consistency model.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -47413,6 +49230,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamPoolCidrs": { @@ -47497,6 +49317,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamResourceCidrs": { @@ -47611,6 +49434,9 @@ "smithy.api#xmlName": "ipamResourceCidrSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetLaunchTemplateData": { @@ -47660,6 +49486,9 @@ "smithy.api#xmlName": "launchTemplateData" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetManagedPrefixListAssociations": { @@ -47747,6 +49576,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetManagedPrefixListEntries": { @@ -47832,6 +49664,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetNetworkInsightsAccessScopeAnalysisFindings": { @@ -47843,7 +49678,13 @@ "target": "com.amazonaws.ec2#GetNetworkInsightsAccessScopeAnalysisFindingsResult" }, "traits": { - "smithy.api#documentation": "

Gets the findings for the specified Network Access Scope analysis.

" + "smithy.api#documentation": "

Gets the findings for the specified Network Access Scope analysis.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "items": "AnalysisFindings", + "pageSize": "MaxResults" + } } }, "com.amazonaws.ec2#GetNetworkInsightsAccessScopeAnalysisFindingsRequest": { @@ -47919,6 +49760,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetNetworkInsightsAccessScopeContent": { @@ -47968,6 +49812,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeContent" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetPasswordData": { @@ -48052,6 +49899,9 @@ "smithy.api#xmlName": "timestamp" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetReservedInstancesExchangeQuote": { @@ -48178,7 +50028,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of GetReservedInstancesExchangeQuote.

" + "smithy.api#documentation": "

Contains the output of GetReservedInstancesExchangeQuote.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetSerialConsoleAccessStatus": { @@ -48222,6 +50073,9 @@ "smithy.api#xmlName": "serialConsoleAccessEnabled" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetSpotPlacementScores": { @@ -48334,6 +50188,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetSubnetCidrReservations": { @@ -48430,6 +50287,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayAttachmentPropagations": { @@ -48514,6 +50374,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayMulticastDomainAssociations": { @@ -48598,6 +50461,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayPolicyTableAssociations": { @@ -48682,6 +50548,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayPolicyTableEntries": { @@ -48752,6 +50621,9 @@ "smithy.api#xmlName": "transitGatewayPolicyTableEntries" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayPrefixListReferences": { @@ -48836,6 +50708,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayRouteTableAssociations": { @@ -48920,6 +50795,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayRouteTablePropagations": { @@ -49004,6 +50882,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVerifiedAccessEndpointPolicy": { @@ -49025,7 +50906,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#required": {} } }, @@ -49059,10 +50940,13 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PolicyDocument", - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

", + "smithy.api#documentation": "

The Verified Access policy document.

", "smithy.api#xmlName": "policyDocument" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVerifiedAccessGroupPolicy": { @@ -49084,7 +50968,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#required": {} } }, @@ -49118,10 +51002,13 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PolicyDocument", - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

", + "smithy.api#documentation": "

The Verified Access policy document.

", "smithy.api#xmlName": "policyDocument" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVpnConnectionDeviceSampleConfiguration": { @@ -49185,6 +51072,9 @@ "smithy.api#xmlName": "vpnConnectionDeviceSampleConfiguration" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVpnConnectionDeviceTypes": { @@ -49252,6 +51142,109 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.ec2#GetVpnTunnelReplacementStatus": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#GetVpnTunnelReplacementStatusRequest" + }, + "output": { + "target": "com.amazonaws.ec2#GetVpnTunnelReplacementStatusResult" + }, + "traits": { + "smithy.api#documentation": "

Get details of available tunnel endpoint maintenance.

" + } + }, + "com.amazonaws.ec2#GetVpnTunnelReplacementStatusRequest": { + "type": "structure", + "members": { + "VpnConnectionId": { + "target": "com.amazonaws.ec2#VpnConnectionId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The ID of the Site-to-Site VPN connection.

", + "smithy.api#required": {} + } + }, + "VpnTunnelOutsideIpAddress": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The external IP address of the VPN tunnel.

", + "smithy.api#required": {} + } + }, + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, and provides an error response. If you have the required permissions, the error response is DryRunOperation. Otherwise, it is UnauthorizedOperation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#GetVpnTunnelReplacementStatusResult": { + "type": "structure", + "members": { + "VpnConnectionId": { + "target": "com.amazonaws.ec2#VpnConnectionId", + "traits": { + "aws.protocols#ec2QueryName": "VpnConnectionId", + "smithy.api#documentation": "

The ID of the Site-to-Site VPN connection.

", + "smithy.api#xmlName": "vpnConnectionId" + } + }, + "TransitGatewayId": { + "target": "com.amazonaws.ec2#TransitGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "TransitGatewayId", + "smithy.api#documentation": "

The ID of the transit gateway associated with the VPN connection.

", + "smithy.api#xmlName": "transitGatewayId" + } + }, + "CustomerGatewayId": { + "target": "com.amazonaws.ec2#CustomerGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "CustomerGatewayId", + "smithy.api#documentation": "

The ID of the customer gateway.

", + "smithy.api#xmlName": "customerGatewayId" + } + }, + "VpnGatewayId": { + "target": "com.amazonaws.ec2#VpnGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "VpnGatewayId", + "smithy.api#documentation": "

The ID of the virtual private gateway.

", + "smithy.api#xmlName": "vpnGatewayId" + } + }, + "VpnTunnelOutsideIpAddress": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "VpnTunnelOutsideIpAddress", + "smithy.api#documentation": "

The external IP address of the VPN tunnel.

", + "smithy.api#xmlName": "vpnTunnelOutsideIpAddress" + } + }, + "MaintenanceDetails": { + "target": "com.amazonaws.ec2#MaintenanceDetails", + "traits": { + "aws.protocols#ec2QueryName": "MaintenanceDetails", + "smithy.api#documentation": "

Get details of pending tunnel endpoint maintenance.

", + "smithy.api#xmlName": "maintenanceDetails" + } + } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GpuDeviceCount": { @@ -50883,7 +52876,7 @@ } }, "Url": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUrl", "traits": { "smithy.api#documentation": "

The URL to the Amazon S3-based disk image being imported. The URL can either be a https URL (https://..) or an\n Amazon S3 URL (s3://..)

" } @@ -51133,6 +53126,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportImage": { @@ -51438,6 +53434,9 @@ "smithy.api#xmlName": "usageOperation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportImageTask": { @@ -51769,6 +53768,9 @@ "smithy.api#xmlName": "conversionTask" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportInstanceTaskDetails": { @@ -51978,6 +53980,15 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.ec2#ImportManifestUrl": { + "type": "string", + "traits": { + "smithy.api#sensitive": {} } }, "com.amazonaws.ec2#ImportSnapshot": { @@ -52094,6 +54105,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportSnapshotTask": { @@ -52248,6 +54262,9 @@ "smithy.api#xmlName": "conversionTask" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportVolumeTaskDetails": { @@ -52461,7 +54478,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PrivateDnsName", - "smithy.api#documentation": "

(IPv4 only) The private DNS hostname name assigned to the instance. This DNS hostname\n can only be used inside the Amazon EC2 network. This name is not available until the\n instance enters the running state.

\n

[EC2-VPC] The Amazon-provided DNS server resolves Amazon-provided private DNS\n hostnames if you've enabled DNS resolution and DNS hostnames in your VPC. If you are not\n using the Amazon-provided DNS server in your VPC, your custom domain name servers must\n resolve the hostname as appropriate.

", + "smithy.api#documentation": "

[IPv4 only] The private DNS hostname name assigned to the instance. This DNS hostname\n can only be used inside the Amazon EC2 network. This name is not available until the\n instance enters the running state.

\n

The Amazon-provided DNS server resolves Amazon-provided private DNS\n hostnames if you've enabled DNS resolution and DNS hostnames in your VPC. If you are not\n using the Amazon-provided DNS server in your VPC, your custom domain name servers must\n resolve the hostname as appropriate.

", "smithy.api#xmlName": "privateDnsName" } }, @@ -52485,7 +54502,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "DnsName", - "smithy.api#documentation": "

(IPv4 only) The public DNS name assigned to the instance. This name is not available\n until the instance enters the running state. For EC2-VPC, this name is only\n available if you've enabled DNS hostnames for your VPC.

", + "smithy.api#documentation": "

[IPv4 only] The public DNS name assigned to the instance. This name is not available\n until the instance enters the running state. This name is only\n available if you've enabled DNS hostnames for your VPC.

", "smithy.api#xmlName": "dnsName" } }, @@ -52525,7 +54542,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "SubnetId", - "smithy.api#documentation": "

[EC2-VPC] The ID of the subnet in which the instance is running.

", + "smithy.api#documentation": "

The ID of the subnet in which the instance is running.

", "smithy.api#xmlName": "subnetId" } }, @@ -52533,7 +54550,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "VpcId", - "smithy.api#documentation": "

[EC2-VPC] The ID of the VPC in which the instance is running.

", + "smithy.api#documentation": "

The ID of the VPC in which the instance is running.

", "smithy.api#xmlName": "vpcId" } }, @@ -52617,7 +54634,7 @@ "target": "com.amazonaws.ec2#ElasticInferenceAcceleratorAssociationList", "traits": { "aws.protocols#ec2QueryName": "ElasticInferenceAcceleratorAssociationSet", - "smithy.api#documentation": "

The elastic inference accelerator associated with the instance.

", + "smithy.api#documentation": "

The elastic inference accelerator associated with the instance.

", "smithy.api#xmlName": "elasticInferenceAcceleratorAssociationSet" } }, @@ -52625,7 +54642,7 @@ "target": "com.amazonaws.ec2#InstanceNetworkInterfaceList", "traits": { "aws.protocols#ec2QueryName": "NetworkInterfaceSet", - "smithy.api#documentation": "

[EC2-VPC] The network interfaces for the instance.

", + "smithy.api#documentation": "

The network interfaces for the instance.

", "smithy.api#xmlName": "networkInterfaceSet" } }, @@ -53241,6 +55258,28 @@ "smithy.api#documentation": "

Information about the number of instances that can be launched onto the Dedicated\n Host.

" } }, + "com.amazonaws.ec2#InstanceConnectEndpointId": { + "type": "string" + }, + "com.amazonaws.ec2#InstanceConnectEndpointMaxResults": { + "type": "integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 1, + "max": 50 + } + } + }, + "com.amazonaws.ec2#InstanceConnectEndpointSet": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#Ec2InstanceConnectEndpoint", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#InstanceCount": { "type": "structure", "members": { @@ -59390,6 +61429,102 @@ "traits": { "smithy.api#enumValue": "r7g.metal" } + }, + "c6in_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "c6in.metal" + } + }, + "m6in_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m6in.metal" + } + }, + "m6idn_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m6idn.metal" + } + }, + "r6in_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r6in.metal" + } + }, + "r6idn_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r6idn.metal" + } + }, + "inf2_xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "inf2.xlarge" + } + }, + "inf2_8xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "inf2.8xlarge" + } + }, + "inf2_24xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "inf2.24xlarge" + } + }, + "inf2_48xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "inf2.48xlarge" + } + }, + "trn1n_32xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "trn1n.32xlarge" + } + }, + "i4g_large": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.large" + } + }, + "i4g_xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.xlarge" + } + }, + "i4g_2xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.2xlarge" + } + }, + "i4g_4xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.4xlarge" + } + }, + "i4g_8xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.8xlarge" + } + }, + "i4g_16xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.16xlarge" + } } } }, @@ -59593,7 +61728,7 @@ "target": "com.amazonaws.ec2#AutoRecoveryFlag", "traits": { "aws.protocols#ec2QueryName": "AutoRecoverySupported", - "smithy.api#documentation": "

Indicates whether auto recovery is supported.

", + "smithy.api#documentation": "

Indicates whether Amazon CloudWatch action based recovery is supported.

", "smithy.api#xmlName": "autoRecoverySupported" } }, @@ -62785,7 +64920,7 @@ "target": "com.amazonaws.ec2#GroupIdentifierList", "traits": { "aws.protocols#ec2QueryName": "GroupSet", - "smithy.api#documentation": "

One or more security groups. When requesting instances in a VPC, you must specify the IDs of the security groups. When requesting instances in EC2-Classic, you can specify the names or the IDs of the security groups.

", + "smithy.api#documentation": "

The IDs of the security groups.

", "smithy.api#xmlName": "groupSet" } }, @@ -62801,7 +64936,7 @@ "target": "com.amazonaws.ec2#BlockDeviceMappingList", "traits": { "aws.protocols#ec2QueryName": "BlockDeviceMapping", - "smithy.api#documentation": "

One or more block device mapping entries.

", + "smithy.api#documentation": "

The block device mapping entries.

", "smithy.api#xmlName": "blockDeviceMapping" } }, @@ -62859,7 +64994,7 @@ "target": "com.amazonaws.ec2#InstanceNetworkInterfaceSpecificationList", "traits": { "aws.protocols#ec2QueryName": "NetworkInterfaceSet", - "smithy.api#documentation": "

One or more network interfaces. If you specify a network interface, you must specify \n subnet IDs and security group IDs using the network interface.

", + "smithy.api#documentation": "

The network interfaces. If you specify a network interface, you must specify \n subnet IDs and security group IDs using the network interface.

", "smithy.api#xmlName": "networkInterfaceSet" } }, @@ -63206,6 +65341,14 @@ "smithy.api#documentation": "

The number of threads per CPU core.

", "smithy.api#xmlName": "threadsPerCore" } + }, + "AmdSevSnp": { + "target": "com.amazonaws.ec2#AmdSevSnpSpecification", + "traits": { + "aws.protocols#ec2QueryName": "AmdSevSnp", + "smithy.api#documentation": "

Indicates whether the instance is enabled for \n AMD SEV-SNP.

", + "smithy.api#xmlName": "amdSevSnp" + } } }, "traits": { @@ -63230,6 +65373,12 @@ "smithy.api#default": 0, "smithy.api#documentation": "

The number of threads per CPU core. To disable multithreading for the instance,\n specify a value of 1. Otherwise, specify the default value of\n 2.

" } + }, + "AmdSevSnp": { + "target": "com.amazonaws.ec2#AmdSevSnpSpecification", + "traits": { + "smithy.api#documentation": "

Indicates whether to enable the instance for AMD SEV-SNP. AMD SEV-SNP is supported \n with M6a, R6a, and C6a instance types only.

" + } } }, "traits": { @@ -64405,7 +66554,7 @@ "target": "com.amazonaws.ec2#Tenancy", "traits": { "aws.protocols#ec2QueryName": "Tenancy", - "smithy.api#documentation": "

The tenancy of the instance (if the instance is running in a VPC). An instance with a\n tenancy of dedicated runs on single-tenant hardware.

", + "smithy.api#documentation": "

The tenancy of the instance. An instance with a\n tenancy of dedicated runs on single-tenant hardware.

", "smithy.api#xmlName": "tenancy" } }, @@ -64478,7 +66627,7 @@ "Tenancy": { "target": "com.amazonaws.ec2#Tenancy", "traits": { - "smithy.api#documentation": "

The tenancy of the instance (if the instance is running in a VPC). An instance with a\n tenancy of dedicated runs on single-tenant hardware.

" + "smithy.api#documentation": "

The tenancy of the instance. An instance with a\n tenancy of dedicated runs on single-tenant hardware.

" } }, "SpreadDomain": { @@ -65014,6 +67163,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ListSnapshotsInRecycleBin": { @@ -65097,6 +67249,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ListingState": { @@ -66086,6 +68241,38 @@ "smithy.api#default": 0 } }, + "com.amazonaws.ec2#MaintenanceDetails": { + "type": "structure", + "members": { + "PendingMaintenance": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "PendingMaintenance", + "smithy.api#documentation": "

Verify existence of a pending maintenance.

", + "smithy.api#xmlName": "pendingMaintenance" + } + }, + "MaintenanceAutoAppliedAfter": { + "target": "com.amazonaws.ec2#MillisecondDateTime", + "traits": { + "aws.protocols#ec2QueryName": "MaintenanceAutoAppliedAfter", + "smithy.api#documentation": "

The timestamp after which Amazon Web Services will automatically apply maintenance.

", + "smithy.api#xmlName": "maintenanceAutoAppliedAfter" + } + }, + "LastMaintenanceApplied": { + "target": "com.amazonaws.ec2#MillisecondDateTime", + "traits": { + "aws.protocols#ec2QueryName": "LastMaintenanceApplied", + "smithy.api#documentation": "

Timestamp of last applied maintenance.

", + "smithy.api#xmlName": "lastMaintenanceApplied" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details for Site-to-Site VPN tunnel endpoint maintenance events.

" + } + }, "com.amazonaws.ec2#ManagedPrefixList": { "type": "structure", "members": { @@ -66493,6 +68680,9 @@ "smithy.api#xmlName": "address" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyAvailabilityZoneGroup": { @@ -66552,6 +68742,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyAvailabilityZoneOptInStatus": { @@ -66654,6 +68847,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyCapacityReservationRequest": { @@ -66727,6 +68923,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyClientVpnEndpoint": { @@ -66857,6 +69056,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyDefaultCreditSpecification": { @@ -66914,6 +69116,9 @@ "smithy.api#xmlName": "instanceFamilyCreditSpecification" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyEbsDefaultKmsKeyId": { @@ -66963,6 +69168,9 @@ "smithy.api#xmlName": "kmsKeyId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyFleet": { @@ -67039,6 +69247,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyFpgaImageAttribute": { @@ -67139,6 +69350,9 @@ "smithy.api#xmlName": "fpgaImageAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyHosts": { @@ -67222,6 +69436,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIdFormat": { @@ -67431,7 +69648,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Modifies the specified attribute of the specified instance. You can specify only one\n attribute at a time.

\n

\n Note: Using this action to change the security groups\n associated with an elastic network interface (ENI) attached to an instance in a VPC can\n result in an error if the instance has more than one ENI. To change the security groups\n associated with an ENI attached to an instance that has multiple ENIs, we recommend that\n you use the ModifyNetworkInterfaceAttribute action.

\n

To modify some attributes, the instance must be stopped. For more information, see\n Modify a stopped instance in the\n Amazon EC2 User Guide.

" + "smithy.api#documentation": "

Modifies the specified attribute of the specified instance. You can specify only one\n attribute at a time.

\n

\n Note: Using this action to change the security groups\n associated with an elastic network interface (ENI) attached to an instance can\n result in an error if the instance has more than one ENI. To change the security groups\n associated with an ENI attached to an instance that has multiple ENIs, we recommend that\n you use the ModifyNetworkInterfaceAttribute action.

\n

To modify some attributes, the instance must be stopped. For more information, see\n Modify a stopped instance in the\n Amazon EC2 User Guide.

" } }, "com.amazonaws.ec2#ModifyInstanceAttributeRequest": { @@ -67496,7 +69713,7 @@ "Groups": { "target": "com.amazonaws.ec2#GroupIdStringList", "traits": { - "smithy.api#documentation": "

[EC2-VPC] Replaces the security groups of the instance with the specified security\n groups. You must specify at least one security group, even if it's just the default\n security group for the VPC. You must specify the security group ID, not the security\n group name.

", + "smithy.api#documentation": "

Replaces the security groups of the instance with the specified security groups.\n You must specify the ID of at least one security group, even if it's just the default\n security group for the VPC.

", "smithy.api#xmlName": "GroupId" } }, @@ -67634,6 +69851,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceCreditSpecification": { @@ -67698,6 +69918,9 @@ "smithy.api#xmlName": "unsuccessfulInstanceCreditSpecificationSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceEventStartTime": { @@ -67763,6 +69986,9 @@ "smithy.api#xmlName": "event" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceEventWindow": { @@ -67831,6 +70057,9 @@ "smithy.api#xmlName": "instanceEventWindow" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceMaintenanceOptions": { @@ -67894,6 +70123,9 @@ "smithy.api#xmlName": "autoRecovery" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceMetadataOptions": { @@ -67983,6 +70215,9 @@ "smithy.api#xmlName": "instanceMetadataOptions" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstancePlacement": { @@ -68078,6 +70313,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpam": { @@ -68199,6 +70437,9 @@ "smithy.api#xmlName": "ipamPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpamRequest": { @@ -68331,6 +70572,9 @@ "smithy.api#xmlName": "ipamResourceCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpamResourceDiscovery": { @@ -68400,6 +70644,9 @@ "smithy.api#xmlName": "ipamResourceDiscovery" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpamResult": { @@ -68413,6 +70660,9 @@ "smithy.api#xmlName": "ipam" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpamScope": { @@ -68468,6 +70718,9 @@ "smithy.api#xmlName": "ipamScope" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyLaunchTemplate": { @@ -68534,6 +70787,9 @@ "smithy.api#xmlName": "launchTemplate" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyLocalGatewayRoute": { @@ -68607,6 +70863,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyManagedPrefixList": { @@ -68692,6 +70951,9 @@ "smithy.api#xmlName": "prefixList" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyNetworkInterfaceAttribute": { @@ -68843,6 +71105,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyReservedInstances": { @@ -68854,7 +71119,7 @@ "target": "com.amazonaws.ec2#ModifyReservedInstancesResult" }, "traits": { - "smithy.api#documentation": "

Modifies the configuration of your Reserved Instances, such as the Availability Zone, \n instance count, or instance type. The Reserved Instances to be modified must be identical, \n except for Availability Zone, network platform, and instance type.

\n

For more information, see Modifying Reserved\n\t\t\t\tInstances in the Amazon EC2 User Guide.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Modifies the configuration of your Reserved Instances, such as the Availability Zone, \n instance count, or instance type. The Reserved Instances to be modified must be identical, \n except for Availability Zone, network platform, and instance type.

\n

For more information, see Modifying Reserved\n\t\t\t\tInstances in the Amazon EC2 User Guide.

" } }, "com.amazonaws.ec2#ModifyReservedInstancesRequest": { @@ -68905,7 +71170,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of ModifyReservedInstances.

" + "smithy.api#documentation": "

Contains the output of ModifyReservedInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifySecurityGroupRules": { @@ -68966,6 +71232,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifySnapshotAttribute": { @@ -69099,6 +71368,9 @@ "smithy.api#xmlName": "tieringStartTime" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifySpotFleetRequest": { @@ -69343,6 +71615,9 @@ "smithy.api#xmlName": "trafficMirrorFilter" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTrafficMirrorFilterRule": { @@ -69457,6 +71732,9 @@ "smithy.api#xmlName": "trafficMirrorFilterRule" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTrafficMirrorSession": { @@ -69555,6 +71833,9 @@ "smithy.api#xmlName": "trafficMirrorSession" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTransitGateway": { @@ -69708,6 +71989,9 @@ "smithy.api#xmlName": "transitGatewayPrefixListReference" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTransitGatewayRequest": { @@ -69757,6 +72041,9 @@ "smithy.api#xmlName": "transitGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTransitGatewayVpcAttachment": { @@ -69850,6 +72137,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpoint": { @@ -69861,7 +72151,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessEndpointResult" }, "traits": { - "smithy.api#documentation": "

Modifies the configuration of an Amazon Web Services Verified Access endpoint.

" + "smithy.api#documentation": "

Modifies the configuration of the specified Amazon Web Services Verified Access endpoint.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointEniOptions": { @@ -69883,7 +72173,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for a network-interface type Verified Access endpoint.

" + "smithy.api#documentation": "

Describes the options when modifying a Verified Access endpoint with the\n network-interface type.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointLoadBalancerOptions": { @@ -69924,7 +72214,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessEndpointPolicyResult" }, "traits": { - "smithy.api#documentation": "

Modifies the specified Verified Access endpoint policy.

" + "smithy.api#documentation": "

Modifies the specified Amazon Web Services Verified Access endpoint policy.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointPolicyRequest": { @@ -69934,7 +72224,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#required": {} } }, @@ -69950,7 +72240,7 @@ "PolicyDocument": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

" + "smithy.api#documentation": "

The Verified Access policy document.

" } }, "ClientToken": { @@ -69990,10 +72280,13 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PolicyDocument", - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

", + "smithy.api#documentation": "

The Verified Access policy document.

", "smithy.api#xmlName": "policyDocument" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointRequest": { @@ -70003,20 +72296,20 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#required": {} } }, "VerifiedAccessGroupId": { "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

" + "smithy.api#documentation": "

The ID of the Verified Access group.

" } }, "LoadBalancerOptions": { "target": "com.amazonaws.ec2#ModifyVerifiedAccessEndpointLoadBalancerOptions", "traits": { - "smithy.api#documentation": "

The load balancer details if creating the Amazon Web Services Verified Access endpoint as\n load-balancertype.

" + "smithy.api#documentation": "

The load balancer details if creating the Verified Access endpoint as\n load-balancertype.

" } }, "NetworkInterfaceOptions": { @@ -70028,7 +72321,7 @@ "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access endpoint.

" + "smithy.api#documentation": "

A description for the Verified Access endpoint.

" } }, "ClientToken": { @@ -70058,10 +72351,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpoint", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessEndpoint", - "smithy.api#documentation": "

The Amazon Web Services Verified Access endpoint details.

", + "smithy.api#documentation": "

The Verified Access endpoint details.

", "smithy.api#xmlName": "verifiedAccessEndpoint" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointSubnetIdList": { @@ -70082,7 +72378,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessGroupResult" }, "traits": { - "smithy.api#documentation": "

Modifies the specified Verified Access group configuration.

" + "smithy.api#documentation": "

Modifies the specified Amazon Web Services Verified Access group configuration.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessGroupPolicy": { @@ -70094,7 +72390,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessGroupPolicyResult" }, "traits": { - "smithy.api#documentation": "

Modifies the specified Verified Access group policy.

" + "smithy.api#documentation": "

Modifies the specified Amazon Web Services Verified Access group policy.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessGroupPolicyRequest": { @@ -70104,7 +72400,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#required": {} } }, @@ -70120,7 +72416,7 @@ "PolicyDocument": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

" + "smithy.api#documentation": "

The Verified Access policy document.

" } }, "ClientToken": { @@ -70160,10 +72456,13 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PolicyDocument", - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

", + "smithy.api#documentation": "

The Verified Access policy document.

", "smithy.api#xmlName": "policyDocument" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessGroupRequest": { @@ -70173,20 +72472,20 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#required": {} } }, "VerifiedAccessInstanceId": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

The ID of the Verified Access instance.

" } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access group.

" + "smithy.api#documentation": "

A description for the Verified Access group.

" } }, "ClientToken": { @@ -70216,10 +72515,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroup", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessGroup", - "smithy.api#documentation": "

Details of Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

Details of Verified Access group.

", "smithy.api#xmlName": "verifiedAccessGroup" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessInstance": { @@ -70231,7 +72533,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessInstanceResult" }, "traits": { - "smithy.api#documentation": "

Modifies the configuration of the specified Verified Access instance.

" + "smithy.api#documentation": "

Modifies the configuration of the specified Amazon Web Services Verified Access instance.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessInstanceLoggingConfiguration": { @@ -70253,7 +72555,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, @@ -70261,7 +72563,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessLogOptions", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The configuration options for Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The configuration options for Verified Access instances.

", "smithy.api#required": {} } }, @@ -70292,10 +72594,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceLoggingConfiguration", "traits": { "aws.protocols#ec2QueryName": "LoggingConfiguration", - "smithy.api#documentation": "

The logging configuration for Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The logging configuration for the Verified Access instance.

", "smithy.api#xmlName": "loggingConfiguration" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessInstanceRequest": { @@ -70305,14 +72610,14 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

A description for the Verified Access instance.

" } }, "DryRun": { @@ -70342,10 +72647,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessTrustProvider": { @@ -70363,6 +72671,42 @@ "com.amazonaws.ec2#ModifyVerifiedAccessTrustProviderOidcOptions": { "type": "structure", "members": { + "Issuer": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The OIDC issuer.

" + } + }, + "AuthorizationEndpoint": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The OIDC authorization endpoint.

" + } + }, + "TokenEndpoint": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The OIDC token endpoint.

" + } + }, + "UserInfoEndpoint": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The OIDC user info endpoint.

" + } + }, + "ClientId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The client identifier.

" + } + }, + "ClientSecret": { + "target": "com.amazonaws.ec2#ClientSecretType", + "traits": { + "smithy.api#documentation": "

The client secret.

" + } + }, "Scope": { "target": "com.amazonaws.ec2#String", "traits": { @@ -70371,7 +72715,7 @@ } }, "traits": { - "smithy.api#documentation": "

OpenID Connect options for an oidc-type, user-identity based trust\n provider.

" + "smithy.api#documentation": "

Options for an OpenID Connect-compatible user-identity trust provider.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessTrustProviderRequest": { @@ -70381,20 +72725,20 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#required": {} } }, "OidcOptions": { "target": "com.amazonaws.ec2#ModifyVerifiedAccessTrustProviderOidcOptions", "traits": { - "smithy.api#documentation": "

The OpenID Connect details for an oidc-type, user-identity based trust provider.

" + "smithy.api#documentation": "

The options for an OpenID Connect-compatible user-identity trust provider.

" } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access trust provider.

" + "smithy.api#documentation": "

A description for the Verified Access trust provider.

" } }, "DryRun": { @@ -70424,10 +72768,13 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVolume": { @@ -70559,6 +72906,9 @@ "smithy.api#xmlName": "volumeModification" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcAttribute": { @@ -70682,6 +73032,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcEndpointRequest": { @@ -70797,6 +73150,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcEndpointServiceConfiguration": { @@ -70912,6 +73268,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcEndpointServicePayerResponsibility": { @@ -70971,6 +73330,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcEndpointServicePermissions": { @@ -71042,6 +73404,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcPeeringConnectionOptions": { @@ -71111,6 +73476,9 @@ "smithy.api#xmlName": "requesterPeeringConnectionOptions" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcTenancy": { @@ -71170,6 +73538,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnConnection": { @@ -71255,6 +73626,9 @@ "smithy.api#xmlName": "vpnConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnConnectionRequest": { @@ -71310,6 +73684,9 @@ "smithy.api#xmlName": "vpnConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnTunnelCertificate": { @@ -71367,6 +73744,9 @@ "smithy.api#xmlName": "vpnConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnTunnelOptions": { @@ -71415,6 +73795,14 @@ "smithy.api#default": false, "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually\n making the request, and provides an error response. If you have the required\n permissions, the error response is DryRunOperation. Otherwise, it is\n UnauthorizedOperation.

" } + }, + "SkipTunnelReplacement": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Choose whether or not to trigger immediate tunnel replacement.

\n

Valid values: True | False\n

" + } } }, "traits": { @@ -71432,6 +73820,9 @@ "smithy.api#xmlName": "vpnConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnTunnelOptionsSpecification": { @@ -71569,6 +73960,14 @@ "traits": { "smithy.api#documentation": "

Options for logging VPN tunnel activity.

" } + }, + "EnableTunnelLifecycleControl": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Turn on or off tunnel endpoint lifecycle control feature.

" + } } }, "traits": { @@ -71625,6 +74024,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#Monitoring": { @@ -71681,7 +74083,7 @@ "target": "com.amazonaws.ec2#MoveAddressToVpcResult" }, "traits": { - "smithy.api#documentation": "

Moves an Elastic IP address from the EC2-Classic platform to the EC2-VPC platform. The\n Elastic IP address must be allocated to your account for more than 24 hours, and it must not\n be associated with an instance. After the Elastic IP address is moved, it is no longer\n available for use in the EC2-Classic platform, unless you move it back using the\n RestoreAddressToClassic request. You cannot move an Elastic IP address that was\n originally allocated for use in the EC2-VPC platform to the EC2-Classic platform.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "\n

This action is deprecated.

\n
\n

Moves an Elastic IP address from the EC2-Classic platform to the EC2-VPC platform. The\n Elastic IP address must be allocated to your account for more than 24 hours, and it must not\n be associated with an instance. After the Elastic IP address is moved, it is no longer\n available for use in the EC2-Classic platform, unless you move it back using the\n RestoreAddressToClassic request. You cannot move an Elastic IP address that was\n originally allocated for use in the EC2-VPC platform to the EC2-Classic platform.

" } }, "com.amazonaws.ec2#MoveAddressToVpcRequest": { @@ -71731,6 +74133,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#MoveByoipCidrToIpam": { @@ -71796,6 +74201,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#MoveStatus": { @@ -71822,7 +74230,7 @@ "target": "com.amazonaws.ec2#MoveStatus", "traits": { "aws.protocols#ec2QueryName": "MoveStatus", - "smithy.api#documentation": "

The status of the Elastic IP address that's being moved to the EC2-VPC platform, or restored to the EC2-Classic platform.

", + "smithy.api#documentation": "

The status of the Elastic IP address that's being moved or restored.

", "smithy.api#xmlName": "moveStatus" } }, @@ -71836,7 +74244,7 @@ } }, "traits": { - "smithy.api#documentation": "

Describes the status of a moving Elastic IP address.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "\n

This action is deprecated.

\n
\n

Describes the status of a moving Elastic IP address.

" } }, "com.amazonaws.ec2#MovingAddressStatusSet": { @@ -72835,7 +75243,7 @@ "target": "com.amazonaws.ec2#ArnList", "traits": { "aws.protocols#ec2QueryName": "FilterInArnSet", - "smithy.api#documentation": "

The Amazon Resource Names (ARN) of the Amazon Web Services resources that the path must traverse.

", + "smithy.api#documentation": "

The Amazon Resource Names (ARN) of the resources that the path must traverse.

", "smithy.api#xmlName": "filterInArnSet" } }, @@ -72996,7 +75404,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "Source", - "smithy.api#documentation": "

The Amazon Web Services resource that is the source of the path.

", + "smithy.api#documentation": "

The ID of the source.

", "smithy.api#xmlName": "source" } }, @@ -73004,7 +75412,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "Destination", - "smithy.api#documentation": "

The Amazon Web Services resource that is the destination of the path.

", + "smithy.api#documentation": "

The ID of the destination.

", "smithy.api#xmlName": "destination" } }, @@ -73028,7 +75436,7 @@ "target": "com.amazonaws.ec2#IpAddress", "traits": { "aws.protocols#ec2QueryName": "SourceIp", - "smithy.api#documentation": "

The IP address of the Amazon Web Services resource that is the source of the path.

", + "smithy.api#documentation": "

The IP address of the source.

", "smithy.api#xmlName": "sourceIp" } }, @@ -73036,7 +75444,7 @@ "target": "com.amazonaws.ec2#IpAddress", "traits": { "aws.protocols#ec2QueryName": "DestinationIp", - "smithy.api#documentation": "

The IP address of the Amazon Web Services resource that is the destination of the path.

", + "smithy.api#documentation": "

The IP address of the destination.

", "smithy.api#xmlName": "destinationIp" } }, @@ -73065,6 +75473,22 @@ "smithy.api#documentation": "

The tags associated with the path.

", "smithy.api#xmlName": "tagSet" } + }, + "FilterAtSource": { + "target": "com.amazonaws.ec2#PathFilter", + "traits": { + "aws.protocols#ec2QueryName": "FilterAtSource", + "smithy.api#documentation": "

Scopes the analysis to network paths that match specific filters at the source.

", + "smithy.api#xmlName": "filterAtSource" + } + }, + "FilterAtDestination": { + "target": "com.amazonaws.ec2#PathFilter", + "traits": { + "aws.protocols#ec2QueryName": "FilterAtDestination", + "smithy.api#documentation": "

Scopes the analysis to network paths that match specific filters at the destination.

", + "smithy.api#xmlName": "filterAtDestination" + } } }, "traits": { @@ -73614,6 +76038,15 @@ } } }, + "com.amazonaws.ec2#NetworkInterfaceIdSet": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#NetworkInterfaceIpv6Address": { "type": "structure", "members": { @@ -74129,7 +76562,7 @@ } }, "ClientSecret": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#ClientSecretType", "traits": { "aws.protocols#ec2QueryName": "ClientSecret", "smithy.api#documentation": "

The client secret.

", @@ -74146,7 +76579,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for OIDC-based, user-identity type trust provider.

" + "smithy.api#documentation": "

Describes the options for an OpenID Connect-compatible user-identity trust\n provider.

" } }, "com.amazonaws.ec2#OnDemandAllocationStrategy": { @@ -74618,6 +77051,30 @@ "smithy.api#documentation": "

The load balancer listener.

", "smithy.api#xmlName": "elasticLoadBalancerListener" } + }, + "FirewallStatelessRule": { + "target": "com.amazonaws.ec2#FirewallStatelessRule", + "traits": { + "aws.protocols#ec2QueryName": "FirewallStatelessRule", + "smithy.api#documentation": "

The Network Firewall stateless rule.

", + "smithy.api#xmlName": "firewallStatelessRule" + } + }, + "FirewallStatefulRule": { + "target": "com.amazonaws.ec2#FirewallStatefulRule", + "traits": { + "aws.protocols#ec2QueryName": "FirewallStatefulRule", + "smithy.api#documentation": "

The Network Firewall stateful rule.

", + "smithy.api#xmlName": "firewallStatefulRule" + } + }, + "ServiceName": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "ServiceName", + "smithy.api#documentation": "

The name of the VPC endpoint service.

", + "smithy.api#xmlName": "serviceName" + } } }, "traits": { @@ -74633,6 +77090,78 @@ } } }, + "com.amazonaws.ec2#PathFilter": { + "type": "structure", + "members": { + "SourceAddress": { + "target": "com.amazonaws.ec2#IpAddress", + "traits": { + "aws.protocols#ec2QueryName": "SourceAddress", + "smithy.api#documentation": "

The source IPv4 address.

", + "smithy.api#xmlName": "sourceAddress" + } + }, + "SourcePortRange": { + "target": "com.amazonaws.ec2#FilterPortRange", + "traits": { + "aws.protocols#ec2QueryName": "SourcePortRange", + "smithy.api#documentation": "

The source port range.

", + "smithy.api#xmlName": "sourcePortRange" + } + }, + "DestinationAddress": { + "target": "com.amazonaws.ec2#IpAddress", + "traits": { + "aws.protocols#ec2QueryName": "DestinationAddress", + "smithy.api#documentation": "

The destination IPv4 address.

", + "smithy.api#xmlName": "destinationAddress" + } + }, + "DestinationPortRange": { + "target": "com.amazonaws.ec2#FilterPortRange", + "traits": { + "aws.protocols#ec2QueryName": "DestinationPortRange", + "smithy.api#documentation": "

The destination port range.

", + "smithy.api#xmlName": "destinationPortRange" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a set of filters for a path analysis. Use path filters to scope the analysis when\n there can be multiple resulting paths.

" + } + }, + "com.amazonaws.ec2#PathRequestFilter": { + "type": "structure", + "members": { + "SourceAddress": { + "target": "com.amazonaws.ec2#IpAddress", + "traits": { + "smithy.api#documentation": "

The source IPv4 address.

" + } + }, + "SourcePortRange": { + "target": "com.amazonaws.ec2#RequestFilterPortRange", + "traits": { + "smithy.api#documentation": "

The source port range.

" + } + }, + "DestinationAddress": { + "target": "com.amazonaws.ec2#IpAddress", + "traits": { + "smithy.api#documentation": "

The destination IPv4 address.

" + } + }, + "DestinationPortRange": { + "target": "com.amazonaws.ec2#RequestFilterPortRange", + "traits": { + "smithy.api#documentation": "

The destination port range.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a set of filters for a path analysis. Use path filters to scope the analysis when\n there can be multiple resulting paths.

" + } + }, "com.amazonaws.ec2#PathStatement": { "type": "structure", "members": { @@ -75274,7 +77803,7 @@ "target": "com.amazonaws.ec2#Tenancy", "traits": { "aws.protocols#ec2QueryName": "Tenancy", - "smithy.api#documentation": "

The tenancy of the instance (if the instance is running in a VPC). An instance with a\n tenancy of dedicated runs on single-tenant hardware.

\n

This parameter is not supported for CreateFleet. The\n host tenancy is not supported for ImportInstance or\n for T3 instances that are configured for the unlimited CPU credit\n option.

", + "smithy.api#documentation": "

The tenancy of the instance. An instance with a\n tenancy of dedicated runs on single-tenant hardware.

\n

This parameter is not supported for CreateFleet. The\n host tenancy is not supported for ImportInstance or\n for T3 instances that are configured for the unlimited CPU credit\n option.

", "smithy.api#xmlName": "tenancy" } }, @@ -76085,6 +78614,16 @@ } } }, + "com.amazonaws.ec2#Priority": { + "type": "integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": -1, + "max": 65535 + } + } + }, "com.amazonaws.ec2#PrivateDnsDetails": { "type": "structure", "members": { @@ -76267,7 +78806,7 @@ "smithy.api#default": 0, "smithy.api#range": { "min": 1, - "max": 7 + "max": 31 } } }, @@ -76333,6 +78872,14 @@ "smithy.api#documentation": "

The speed of the processor, in GHz.

", "smithy.api#xmlName": "sustainedClockSpeedInGhz" } + }, + "SupportedFeatures": { + "target": "com.amazonaws.ec2#SupportedAdditionalProcessorFeatureList", + "traits": { + "aws.protocols#ec2QueryName": "SupportedFeatures", + "smithy.api#documentation": "

Indicates whether the instance type supports AMD SEV-SNP. If the request returns \n amd-sev-snp, AMD SEV-SNP is supported. Otherwise, it is not supported.

", + "smithy.api#xmlName": "supportedFeatures" + } } }, "traits": { @@ -76449,6 +78996,25 @@ } } }, + "com.amazonaws.ec2#ProtocolInt": { + "type": "integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 0, + "max": 255 + } + } + }, + "com.amazonaws.ec2#ProtocolIntList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#ProtocolInt", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#ProtocolList": { "type": "list", "member": { @@ -76551,6 +79117,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ProvisionIpamPoolCidr": { @@ -76627,6 +79196,9 @@ "smithy.api#xmlName": "ipamPoolCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ProvisionPublicIpv4PoolCidr": { @@ -76701,6 +79273,9 @@ "smithy.api#xmlName": "poolAddressRange" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ProvisionedBandwidth": { @@ -77112,6 +79687,9 @@ "smithy.api#xmlName": "totalUpfrontPrice" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#PurchaseRequest": { @@ -77162,7 +79740,7 @@ "target": "com.amazonaws.ec2#PurchaseReservedInstancesOfferingResult" }, "traits": { - "smithy.api#documentation": "

Purchases a Reserved Instance for use with your account. With Reserved Instances, you pay a lower \n hourly rate compared to On-Demand instance pricing.

\n

Use DescribeReservedInstancesOfferings to get a list of Reserved Instance offerings \n\t\t\tthat match your specifications. After you've purchased a Reserved Instance, you can check for your\n\t\t\tnew Reserved Instance with DescribeReservedInstances.

\n

To queue a purchase for a future date and time, specify a purchase time. If you do not specify a\n purchase time, the default is the current time.

\n

For more information, see Reserved Instances and \n \t Reserved Instance Marketplace \n \t in the Amazon EC2 User Guide.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Purchases a Reserved Instance for use with your account. With Reserved Instances, you pay a lower \n hourly rate compared to On-Demand instance pricing.

\n

Use DescribeReservedInstancesOfferings to get a list of Reserved Instance offerings \n\t\t\tthat match your specifications. After you've purchased a Reserved Instance, you can check for your\n\t\t\tnew Reserved Instance with DescribeReservedInstances.

\n

To queue a purchase for a future date and time, specify a purchase time. If you do not specify a\n purchase time, the default is the current time.

\n

For more information, see Reserved Instances and \n \t Reserved Instance Marketplace \n \t in the Amazon EC2 User Guide.

" } }, "com.amazonaws.ec2#PurchaseReservedInstancesOfferingRequest": { @@ -77228,7 +79806,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of PurchaseReservedInstancesOffering.

" + "smithy.api#documentation": "

Contains the output of PurchaseReservedInstancesOffering.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#PurchaseScheduledInstances": { @@ -77289,7 +79868,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of PurchaseScheduledInstances.

" + "smithy.api#documentation": "

Contains the output of PurchaseScheduledInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#PurchaseSet": { @@ -77710,7 +80290,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of RegisterImage.

" + "smithy.api#documentation": "

Contains the output of RegisterImage.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#RegisterInstanceEventNotificationAttributes": { @@ -77739,7 +80320,9 @@ "InstanceTagAttribute": { "target": "com.amazonaws.ec2#RegisterInstanceTagAttributeRequest", "traits": { - "smithy.api#documentation": "

Information about the tag keys to register.

" + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

Information about the tag keys to register.

", + "smithy.api#required": {} } } }, @@ -77758,6 +80341,9 @@ "smithy.api#xmlName": "instanceTagAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RegisterInstanceTagAttributeRequest": { @@ -77844,6 +80430,9 @@ "smithy.api#xmlName": "registeredMulticastGroupMembers" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RegisterTransitGatewayMulticastGroupSources": { @@ -77907,6 +80496,9 @@ "smithy.api#xmlName": "registeredMulticastGroupSources" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectTransitGatewayMulticastDomainAssociations": { @@ -77966,6 +80558,9 @@ "smithy.api#xmlName": "associations" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectTransitGatewayPeeringAttachment": { @@ -78015,6 +80610,9 @@ "smithy.api#xmlName": "transitGatewayPeeringAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectTransitGatewayVpcAttachment": { @@ -78064,6 +80662,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectVpcEndpointConnections": { @@ -78122,6 +80723,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectVpcPeeringConnection": { @@ -78177,6 +80781,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReleaseAddress": { @@ -78188,7 +80795,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Releases the specified Elastic IP address.

\n

[EC2-Classic, default VPC] Releasing an Elastic IP address automatically disassociates it\n\t\t\t\tfrom any instance that it's associated with. To disassociate an Elastic IP address without\n\t\t\t\treleasing it, use DisassociateAddress.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
\n

[Nondefault VPC] You must use DisassociateAddress to disassociate the Elastic IP address\n\t\t\t before you can release it. Otherwise, Amazon EC2 returns an error (InvalidIPAddress.InUse).

\n

After releasing an Elastic IP address, it is released to the IP address pool. \n Be sure to update your DNS records and any servers or devices that communicate with the address. \n If you attempt to release an Elastic IP address that you already released, you'll get an\n AuthFailure error if the address is already allocated to another Amazon Web Services account.

\n

[EC2-VPC] After you release an Elastic IP address for use in a VPC, you might be able to recover it.\n For more information, see AllocateAddress.

\n

For more\n information, see Elastic IP\n Addresses in the Amazon Elastic Compute Cloud User Guide.

" + "smithy.api#documentation": "

Releases the specified Elastic IP address.

\n

[Default VPC] Releasing an Elastic IP address automatically disassociates it\n\t\t\t\tfrom any instance that it's associated with. To disassociate an Elastic IP address without\n\t\t\t\treleasing it, use DisassociateAddress.

\n

[Nondefault VPC] You must use DisassociateAddress to disassociate the Elastic IP address\n\t\t\t before you can release it. Otherwise, Amazon EC2 returns an error (InvalidIPAddress.InUse).

\n

After releasing an Elastic IP address, it is released to the IP address pool. \n Be sure to update your DNS records and any servers or devices that communicate with the address. \n If you attempt to release an Elastic IP address that you already released, you'll get an\n AuthFailure error if the address is already allocated to another Amazon Web Services account.

\n

After you release an Elastic IP address, you might be able to recover it.\n For more information, see AllocateAddress.

" } }, "com.amazonaws.ec2#ReleaseAddressRequest": { @@ -78197,13 +80804,13 @@ "AllocationId": { "target": "com.amazonaws.ec2#AllocationId", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The allocation ID. Required for EC2-VPC.

" + "smithy.api#documentation": "

The allocation ID. This parameter is required.

" } }, "PublicIp": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

[EC2-Classic] The Elastic IP address. Required for EC2-Classic.

" + "smithy.api#documentation": "

Deprecated.

" } }, "NetworkBorderGroup": { @@ -78276,6 +80883,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReleaseIpamPoolAllocation": { @@ -78287,7 +80897,7 @@ "target": "com.amazonaws.ec2#ReleaseIpamPoolAllocationResult" }, "traits": { - "smithy.api#documentation": "

Release an allocation within an IPAM pool. You can only use this action to release manual allocations. To remove an allocation for a resource without deleting the resource, set its monitored state to false using ModifyIpamResourceCidr. For more information, see Release an allocation in the Amazon VPC IPAM User Guide.\n

" + "smithy.api#documentation": "

Release an allocation within an IPAM pool. The Region you use should be the IPAM pool locale. The locale is the Amazon Web Services Region where this IPAM pool is available for allocations. You can only use this action to release manual allocations. To remove an allocation for a resource without deleting the resource, set its monitored state to false using ModifyIpamResourceCidr. For more information, see Release an allocation in the Amazon VPC IPAM User Guide.\n

\n \n

All EC2 API actions follow an eventual consistency model.

\n
" } }, "com.amazonaws.ec2#ReleaseIpamPoolAllocationRequest": { @@ -78343,6 +80953,9 @@ "smithy.api#xmlName": "success" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RemoveIpamOperatingRegion": { @@ -78446,6 +81059,9 @@ "smithy.api#xmlName": "iamInstanceProfileAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplaceNetworkAclAssociation": { @@ -78509,6 +81125,9 @@ "smithy.api#xmlName": "newAssociationId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplaceNetworkAclEntry": { @@ -78985,6 +81604,9 @@ "smithy.api#xmlName": "associationState" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplaceTransitGatewayRoute": { @@ -79056,6 +81678,79 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.ec2#ReplaceVpnTunnel": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#ReplaceVpnTunnelRequest" + }, + "output": { + "target": "com.amazonaws.ec2#ReplaceVpnTunnelResult" + }, + "traits": { + "smithy.api#documentation": "

Trigger replacement of specified VPN tunnel.

" + } + }, + "com.amazonaws.ec2#ReplaceVpnTunnelRequest": { + "type": "structure", + "members": { + "VpnConnectionId": { + "target": "com.amazonaws.ec2#VpnConnectionId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The ID of the Site-to-Site VPN connection.

", + "smithy.api#required": {} + } + }, + "VpnTunnelOutsideIpAddress": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The external IP address of the VPN tunnel.

", + "smithy.api#required": {} + } + }, + "ApplyPendingMaintenance": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Trigger pending tunnel endpoint maintenance.

" + } + }, + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, and provides an error response. If you have the required permissions, the error response is DryRunOperation. Otherwise, it is UnauthorizedOperation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#ReplaceVpnTunnelResult": { + "type": "structure", + "members": { + "Return": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "aws.protocols#ec2QueryName": "Return", + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Confirmation of replace tunnel operation.

", + "smithy.api#xmlName": "return" + } + } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplacementStrategy": { @@ -79235,6 +81930,30 @@ } } }, + "com.amazonaws.ec2#RequestFilterPortRange": { + "type": "structure", + "members": { + "FromPort": { + "target": "com.amazonaws.ec2#Port", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The first port in the range.

" + } + }, + "ToPort": { + "target": "com.amazonaws.ec2#Port", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The last port in the range.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a port range.

" + } + }, "com.amazonaws.ec2#RequestHostIdList": { "type": "list", "member": { @@ -79334,7 +82053,7 @@ "ImageId": { "target": "com.amazonaws.ec2#ImageId", "traits": { - "smithy.api#documentation": "

The ID of the AMI. Alternatively, you can specify a Systems Manager parameter, which\n will resolve to an AMI ID on launch.

\n

Valid formats:

\n
    \n
  • \n

    \n ami-17characters00000\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name:version-number\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name:label\n

    \n
  • \n
\n

For more information, see Use a Systems Manager parameter to find an AMI in the Amazon Elastic Compute Cloud User Guide.

" + "smithy.api#documentation": "

The ID of the AMI. Alternatively, you can specify a Systems Manager parameter, which\n will resolve to an AMI ID on launch.

\n

Valid formats:

\n
    \n
  • \n

    \n ami-17characters00000\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name:version-number\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name:label\n

    \n
  • \n
  • \n

    \n resolve:ssm:public-parameter\n

    \n
  • \n
\n \n

Currently, EC2 Fleet and Spot Fleet do not support specifying a Systems Manager parameter. \n If the launch template will be used by an EC2 Fleet or Spot Fleet, you must specify the AMI ID.

\n
\n

For more information, see Use a Systems Manager parameter instead of an AMI ID in the Amazon Elastic Compute Cloud User Guide.

" } }, "InstanceType": { @@ -79382,7 +82101,7 @@ } }, "UserData": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUserData", "traits": { "smithy.api#documentation": "

The user data to make available to the instance. You must provide base64-encoded text.\n User data is limited to 16 KB. For more information, see Run commands on your Linux instance at\n launch (Linux) or Work with instance\n user data (Windows) in the Amazon Elastic Compute Cloud User Guide.

\n

If you are creating the launch template for use with Batch, the user\n data must be provided in the MIME multi-part archive format. For more information, see Amazon EC2 user data in launch templates in the Batch User Guide.

" } @@ -79499,8 +82218,7 @@ } }, "traits": { - "smithy.api#documentation": "

The information to include in the launch template.

\n \n

You must specify at least one parameter for the launch template data.

\n
", - "smithy.api#sensitive": {} + "smithy.api#documentation": "

The information to include in the launch template.

\n \n

You must specify at least one parameter for the launch template data.

\n
" } }, "com.amazonaws.ec2#RequestSpotFleet": { @@ -79570,7 +82288,7 @@ "target": "com.amazonaws.ec2#RequestSpotInstancesResult" }, "traits": { - "smithy.api#documentation": "

Creates a Spot Instance request.

\n

For more information, see Spot Instance requests in\n the Amazon EC2 User Guide for Linux Instances.

\n \n

We strongly discourage using the RequestSpotInstances API because it is a legacy\n API with no planned investment. For options for requesting Spot Instances, see\n Which\n is the best Spot request method to use? in the\n Amazon EC2 User Guide for Linux Instances.

\n
\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon EC2 User Guide for Linux Instances.

\n
" + "smithy.api#documentation": "

Creates a Spot Instance request.

\n

For more information, see Spot Instance requests in\n the Amazon EC2 User Guide for Linux Instances.

\n \n

We strongly discourage using the RequestSpotInstances API because it is a legacy\n API with no planned investment. For options for requesting Spot Instances, see\n Which\n is the best Spot request method to use? in the\n Amazon EC2 User Guide for Linux Instances.

\n
" } }, "com.amazonaws.ec2#RequestSpotInstancesRequest": { @@ -79694,13 +82412,14 @@ "target": "com.amazonaws.ec2#SpotInstanceRequestList", "traits": { "aws.protocols#ec2QueryName": "SpotInstanceRequestSet", - "smithy.api#documentation": "

One or more Spot Instance requests.

", + "smithy.api#documentation": "

The Spot Instance requests.

", "smithy.api#xmlName": "spotInstanceRequestSet" } } }, "traits": { - "smithy.api#documentation": "

Contains the output of RequestSpotInstances.

" + "smithy.api#documentation": "

Contains the output of RequestSpotInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#RequestSpotLaunchSpecification": { @@ -79709,14 +82428,14 @@ "SecurityGroupIds": { "target": "com.amazonaws.ec2#RequestSpotLaunchSpecificationSecurityGroupIdList", "traits": { - "smithy.api#documentation": "

One or more security group IDs.

", + "smithy.api#documentation": "

The IDs of the security groups.

", "smithy.api#xmlName": "SecurityGroupId" } }, "SecurityGroups": { "target": "com.amazonaws.ec2#RequestSpotLaunchSpecificationSecurityGroupList", "traits": { - "smithy.api#documentation": "

One or more security groups. When requesting instances in a VPC, you must specify the IDs of the security groups. When requesting instances in EC2-Classic, you can specify the names or the IDs of the security groups.

", + "smithy.api#documentation": "

Not supported.

", "smithy.api#xmlName": "SecurityGroup" } }, @@ -79732,7 +82451,7 @@ "target": "com.amazonaws.ec2#BlockDeviceMappingList", "traits": { "aws.protocols#ec2QueryName": "BlockDeviceMapping", - "smithy.api#documentation": "

One or more block device mapping entries. You can't specify both a snapshot ID and an encryption value. \n This is because only blank volumes can be encrypted on creation. If a snapshot is the basis for a volume, \n it is not blank and its encryption status is used for the volume encryption status.

", + "smithy.api#documentation": "

The block device mapping entries. You can't specify both a snapshot ID and an encryption value. \n This is because only blank volumes can be encrypted on creation. If a snapshot is the basis for a volume, \n it is not blank and its encryption status is used for the volume encryption status.

", "smithy.api#xmlName": "blockDeviceMapping" } }, @@ -79797,7 +82516,7 @@ "NetworkInterfaces": { "target": "com.amazonaws.ec2#InstanceNetworkInterfaceSpecificationList", "traits": { - "smithy.api#documentation": "

One or more network interfaces. If you specify a network interface, you must specify \n subnet IDs and security group IDs using the network interface.

", + "smithy.api#documentation": "

The network interfaces. If you specify a network interface, you must specify \n subnet IDs and security group IDs using the network interface.

", "smithy.api#xmlName": "NetworkInterface" } }, @@ -79863,7 +82582,7 @@ "target": "com.amazonaws.ec2#GroupIdentifierList", "traits": { "aws.protocols#ec2QueryName": "GroupSet", - "smithy.api#documentation": "

[EC2-Classic only] The security groups.

", + "smithy.api#documentation": "

Not supported.

", "smithy.api#xmlName": "groupSet" } }, @@ -80337,7 +83056,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "Platform", - "smithy.api#documentation": "

The network platform of the modified Reserved Instances, which is either EC2-Classic or EC2-VPC.

", + "smithy.api#documentation": "

The network platform of the modified Reserved Instances.

", "smithy.api#xmlName": "platform" } }, @@ -80849,6 +83568,9 @@ "smithy.api#xmlName": "address" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ResetEbsDefaultKmsKeyId": { @@ -80890,6 +83612,9 @@ "smithy.api#xmlName": "kmsKeyId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ResetFpgaImageAttribute": { @@ -80958,6 +83683,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ResetImageAttribute": { @@ -81745,6 +84473,12 @@ "traits": { "smithy.api#enumValue": "ipam-resource-discovery-association" } + }, + "instance_connect_endpoint": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "instance-connect-endpoint" + } } } }, @@ -82067,7 +84801,7 @@ "target": "com.amazonaws.ec2#RestoreAddressToClassicResult" }, "traits": { - "smithy.api#documentation": "

Restores an Elastic IP address that was previously moved to the EC2-VPC platform back to the EC2-Classic platform. You cannot move an Elastic IP address that was originally allocated for use in EC2-VPC. The Elastic IP address must not be associated with an instance or network interface.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "\n

This action is deprecated.

\n
\n

Restores an Elastic IP address that was previously moved to the EC2-VPC platform back to the EC2-Classic platform. You cannot move an Elastic IP address that was originally allocated for use in EC2-VPC. The Elastic IP address must not be associated with an instance or network interface.

" } }, "com.amazonaws.ec2#RestoreAddressToClassicRequest": { @@ -82117,6 +84851,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RestoreImageFromRecycleBin": { @@ -82168,6 +84905,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RestoreManagedPrefixListVersion": { @@ -82235,6 +84975,9 @@ "smithy.api#xmlName": "prefixList" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RestoreSnapshotFromRecycleBin": { @@ -82360,6 +85103,9 @@ "smithy.api#xmlName": "volumeSize" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RestoreSnapshotTier": { @@ -82454,6 +85200,9 @@ "smithy.api#xmlName": "isPermanentRestore" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ResultRange": { @@ -82535,6 +85284,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RevokeSecurityGroupEgress": { @@ -82665,6 +85417,9 @@ "smithy.api#xmlName": "unknownIpPermissionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RevokeSecurityGroupIngress": { @@ -82783,6 +85538,9 @@ "smithy.api#xmlName": "unknownIpPermissionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RoleId": { @@ -83233,6 +85991,105 @@ } } }, + "com.amazonaws.ec2#RuleGroupRuleOptionsPair": { + "type": "structure", + "members": { + "RuleGroupArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupArn", + "smithy.api#documentation": "

The ARN of the rule group.

", + "smithy.api#xmlName": "ruleGroupArn" + } + }, + "RuleOptions": { + "target": "com.amazonaws.ec2#RuleOptionList", + "traits": { + "aws.protocols#ec2QueryName": "RuleOptionSet", + "smithy.api#documentation": "

The rule options.

", + "smithy.api#xmlName": "ruleOptionSet" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes the rule options for a stateful rule group.

" + } + }, + "com.amazonaws.ec2#RuleGroupRuleOptionsPairList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#RuleGroupRuleOptionsPair", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, + "com.amazonaws.ec2#RuleGroupTypePair": { + "type": "structure", + "members": { + "RuleGroupArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupArn", + "smithy.api#documentation": "

The ARN of the rule group.

", + "smithy.api#xmlName": "ruleGroupArn" + } + }, + "RuleGroupType": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupType", + "smithy.api#documentation": "

The rule group type. The possible values are Domain List and Suricata.

", + "smithy.api#xmlName": "ruleGroupType" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes the type of a stateful rule group.

" + } + }, + "com.amazonaws.ec2#RuleGroupTypePairList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#RuleGroupTypePair", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, + "com.amazonaws.ec2#RuleOption": { + "type": "structure", + "members": { + "Keyword": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "Keyword", + "smithy.api#documentation": "

The Suricata keyword.

", + "smithy.api#xmlName": "keyword" + } + }, + "Settings": { + "target": "com.amazonaws.ec2#StringList", + "traits": { + "aws.protocols#ec2QueryName": "SettingSet", + "smithy.api#documentation": "

The settings for the keyword.

", + "smithy.api#xmlName": "settingSet" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes additional settings for a stateful rule.

" + } + }, + "com.amazonaws.ec2#RuleOptionList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#RuleOption", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#RunInstances": { "type": "operation", "input": { @@ -83242,7 +86099,7 @@ "target": "com.amazonaws.ec2#Reservation" }, "traits": { - "smithy.api#documentation": "

Launches the specified number of instances using an AMI for which you have\n permissions.

\n

You can specify a number of options, or leave the default options. The following rules\n apply:

\n
    \n
  • \n

    [EC2-VPC] If you don't specify a subnet ID, we choose a default subnet from\n your default VPC for you. If you don't have a default VPC, you must specify a\n subnet ID in the request.

    \n
  • \n
  • \n

    [EC2-Classic] If don't specify an Availability Zone, we choose one for\n you.

    \n
  • \n
  • \n

    Some instance types must be launched into a VPC. If you do not have a default\n VPC, or if you do not specify a subnet ID, the request fails. For more\n information, see Instance types available only in a VPC.

    \n
  • \n
  • \n

    [EC2-VPC] All instances have a network interface with a primary private IPv4\n address. If you don't specify this address, we choose one from the IPv4 range of\n your subnet.

    \n
  • \n
  • \n

    Not all instance types support IPv6 addresses. For more information, see\n Instance\n types.

    \n
  • \n
  • \n

    If you don't specify a security group ID, we use the default security group.\n For more information, see Security\n groups.

    \n
  • \n
  • \n

    If any of the AMIs have a product code attached for which the user has not\n subscribed, the request fails.

    \n
  • \n
\n

You can create a launch template,\n which is a resource that contains the parameters to launch an instance. When you launch\n an instance using RunInstances, you can specify the launch template\n instead of specifying the launch parameters.

\n

To ensure faster instance launches, break up large requests into smaller batches. For\n example, create five separate launch requests for 100 instances each instead of one\n launch request for 500 instances.

\n

An instance is ready for you to use when it's in the running state. You\n can check the state of your instance using DescribeInstances. You can\n tag instances and EBS volumes during launch, after launch, or both. For more\n information, see CreateTags and Tagging your Amazon EC2\n resources.

\n

Linux instances have access to the public key of the key pair at boot. You can use\n this key to provide secure access to the instance. Amazon EC2 public images use this\n feature to provide secure access without passwords. For more information, see Key\n pairs.

\n

For troubleshooting, see What to do if\n an instance immediately terminates, and Troubleshooting connecting to your instance.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a\n VPC. For more information, see Migrate from EC2-Classic to a\n VPC in the Amazon EC2 User Guide.

\n
" + "smithy.api#documentation": "

Launches the specified number of instances using an AMI for which you have\n permissions.

\n

You can specify a number of options, or leave the default options. The following rules\n apply:

\n
    \n
  • \n

    If you don't specify a subnet ID, we choose a default subnet from\n your default VPC for you. If you don't have a default VPC, you must specify a\n subnet ID in the request.

    \n
  • \n
  • \n

    All instances have a network interface with a primary private IPv4\n address. If you don't specify this address, we choose one from the IPv4 range of\n your subnet.

    \n
  • \n
  • \n

    Not all instance types support IPv6 addresses. For more information, see\n Instance\n types.

    \n
  • \n
  • \n

    If you don't specify a security group ID, we use the default security group.\n For more information, see Security\n groups.

    \n
  • \n
  • \n

    If any of the AMIs have a product code attached for which the user has not\n subscribed, the request fails.

    \n
  • \n
\n

You can create a launch template,\n which is a resource that contains the parameters to launch an instance. When you launch\n an instance using RunInstances, you can specify the launch template\n instead of specifying the launch parameters.

\n

To ensure faster instance launches, break up large requests into smaller batches. For\n example, create five separate launch requests for 100 instances each instead of one\n launch request for 500 instances.

\n

An instance is ready for you to use when it's in the running state. You\n can check the state of your instance using DescribeInstances. You can\n tag instances and EBS volumes during launch, after launch, or both. For more\n information, see CreateTags and Tagging your Amazon EC2\n resources.

\n

Linux instances have access to the public key of the key pair at boot. You can use\n this key to provide secure access to the instance. Amazon EC2 public images use this\n feature to provide secure access without passwords. For more information, see Key\n pairs.

\n

For troubleshooting, see What to do if\n an instance immediately terminates, and Troubleshooting connecting to your instance.

" } }, "com.amazonaws.ec2#RunInstancesMonitoringEnabled": { @@ -83291,13 +86148,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

[EC2-VPC] The number of IPv6 addresses to associate with the primary network\n interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet. You\n cannot specify this option and the option to assign specific IPv6 addresses in the same\n request. You can specify this option if you've specified a minimum number of instances\n to launch.

\n

You cannot specify this option and the network interfaces option in the same\n request.

" + "smithy.api#documentation": "

The number of IPv6 addresses to associate with the primary network\n interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet. You\n cannot specify this option and the option to assign specific IPv6 addresses in the same\n request. You can specify this option if you've specified a minimum number of instances\n to launch.

\n

You cannot specify this option and the network interfaces option in the same\n request.

" } }, "Ipv6Addresses": { "target": "com.amazonaws.ec2#InstanceIpv6AddressList", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The IPv6 addresses from the range of the subnet to associate with the\n primary network interface. You cannot specify this option and the option to assign a\n number of IPv6 addresses in the same request. You cannot specify this option if you've\n specified a minimum number of instances to launch.

\n

You cannot specify this option and the network interfaces option in the same\n request.

", + "smithy.api#documentation": "

The IPv6 addresses from the range of the subnet to associate with the\n primary network interface. You cannot specify this option and the option to assign a\n number of IPv6 addresses in the same request. You cannot specify this option if you've\n specified a minimum number of instances to launch.

\n

You cannot specify this option and the network interfaces option in the same\n request.

", "smithy.api#xmlName": "Ipv6Address" } }, @@ -83359,14 +86216,14 @@ "SecurityGroups": { "target": "com.amazonaws.ec2#SecurityGroupStringList", "traits": { - "smithy.api#documentation": "

[EC2-Classic, default VPC] The names of the security groups.

\n

If you specify a network interface, you must specify any security groups as part of\n the network interface.

\n

Default: Amazon EC2 uses the default security group.

", + "smithy.api#documentation": "

[Default VPC] The names of the security groups.

\n

If you specify a network interface, you must specify any security groups as part of\n the network interface.

\n

Default: Amazon EC2 uses the default security group.

", "smithy.api#xmlName": "SecurityGroup" } }, "SubnetId": { "target": "com.amazonaws.ec2#SubnetId", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The ID of the subnet to launch the instance into.

\n

If you specify a network interface, you must specify any subnets as part of the\n network interface.

" + "smithy.api#documentation": "

The ID of the subnet to launch the instance into.

\n

If you specify a network interface, you must specify any subnets as part of the\n network interface.

" } }, "UserData": { @@ -83450,7 +86307,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PrivateIpAddress", - "smithy.api#documentation": "

[EC2-VPC] The primary IPv4 address. You must specify a value from the IPv4 address\n range of the subnet.

\n

Only one private IP address can be designated as primary. You can't specify this\n option if you've specified the option to designate a private IP address as the primary\n IP address in a network interface specification. You cannot specify this option if\n you're launching more than one instance in the request.

\n

You cannot specify this option and the network interfaces option in the same\n request.

", + "smithy.api#documentation": "

The primary IPv4 address. You must specify a value from the IPv4 address\n range of the subnet.

\n

Only one private IP address can be designated as primary. You can't specify this\n option if you've specified the option to designate a private IP address as the primary\n IP address in a network interface specification. You cannot specify this option if\n you're launching more than one instance in the request.

\n

You cannot specify this option and the network interfaces option in the same\n request.

", "smithy.api#xmlName": "privateIpAddress" } }, @@ -83463,7 +86320,7 @@ "ElasticInferenceAccelerators": { "target": "com.amazonaws.ec2#ElasticInferenceAccelerators", "traits": { - "smithy.api#documentation": "

An elastic inference accelerator to associate with the instance. Elastic inference\n accelerators are a resource you can attach to your Amazon EC2 instances to accelerate\n your Deep Learning (DL) inference workloads.

\n

You cannot specify accelerators from different generations in the same request.

", + "smithy.api#documentation": "

An elastic inference accelerator to associate with the instance. Elastic inference\n accelerators are a resource you can attach to your Amazon EC2 instances to accelerate\n your Deep Learning (DL) inference workloads.

\n

You cannot specify accelerators from different generations in the same request.

\n \n

Starting April 15, 2023, Amazon Web Services will not onboard new customers to Amazon\n Elastic Inference (EI), and will help current customers migrate their workloads to\n options that offer better price and performance. After April 15, 2023, new customers\n will not be able to launch instances with Amazon EI accelerators in Amazon SageMaker,\n Amazon ECS, or Amazon EC2. However, customers who have used Amazon EI at least once during\n the past 30-day period are considered current customers and will be able to continue\n using the service.

\n
", "smithy.api#xmlName": "ElasticInferenceAccelerator" } }, @@ -83633,7 +86490,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of RunScheduledInstances.

" + "smithy.api#documentation": "

Contains the output of RunScheduledInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#S3ObjectTag": { @@ -83760,7 +86618,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NetworkPlatform", - "smithy.api#documentation": "

The network platform (EC2-Classic or EC2-VPC).

", + "smithy.api#documentation": "

The network platform.

", "smithy.api#xmlName": "networkPlatform" } }, @@ -83914,7 +86772,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NetworkPlatform", - "smithy.api#documentation": "

The network platform (EC2-Classic or EC2-VPC).

", + "smithy.api#documentation": "

The network platform.

", "smithy.api#xmlName": "networkPlatform" } }, @@ -84577,6 +87435,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#SearchTransitGatewayMulticastGroups": { @@ -84661,6 +87522,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#SearchTransitGatewayRoutes": { @@ -84737,6 +87601,9 @@ "smithy.api#xmlName": "additionalRoutesAvailable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#SecurityGroup": { @@ -84823,6 +87690,15 @@ } } }, + "com.amazonaws.ec2#SecurityGroupIdSet": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#SecurityGroupId", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#SecurityGroupIdStringList": { "type": "list", "member": { @@ -84832,6 +87708,21 @@ } } }, + "com.amazonaws.ec2#SecurityGroupIdStringListRequest": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#SecurityGroupId", + "traits": { + "smithy.api#xmlName": "SecurityGroupId" + } + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 16 + } + } + }, "com.amazonaws.ec2#SecurityGroupIdentifier": { "type": "structure", "members": { @@ -85230,6 +88121,12 @@ "smithy.api#input": {} } }, + "com.amazonaws.ec2#SensitiveUrl": { + "type": "string", + "traits": { + "smithy.api#sensitive": {} + } + }, "com.amazonaws.ec2#SensitiveUserData": { "type": "string", "traits": { @@ -85916,7 +88813,7 @@ } }, "Url": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUrl", "traits": { "aws.protocols#ec2QueryName": "Url", "smithy.api#documentation": "

The URL used to access the disk image.

", @@ -85961,7 +88858,7 @@ } }, "Url": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUrl", "traits": { "smithy.api#documentation": "

The URL to the Amazon S3-based disk image being imported. It can either be a https URL (https://..) or an Amazon\n S3 URL (s3://..).

" } @@ -86279,7 +89176,7 @@ } }, "Url": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUrl", "traits": { "aws.protocols#ec2QueryName": "Url", "smithy.api#documentation": "

The URL of the disk image from which the snapshot is created.

", @@ -86521,7 +89418,7 @@ "target": "com.amazonaws.ec2#GroupIdentifierList", "traits": { "aws.protocols#ec2QueryName": "GroupSet", - "smithy.api#documentation": "

One or more security groups. When requesting instances in a VPC, you must specify the IDs of the security groups. When requesting instances in EC2-Classic, you can specify the names or the IDs of the security groups.

", + "smithy.api#documentation": "

The security groups.

", "smithy.api#xmlName": "groupSet" } }, @@ -87913,6 +90810,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#StartNetworkInsightsAccessScopeAnalysis": { @@ -87978,6 +90878,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeAnalysis" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#StartNetworkInsightsAnalysis": { @@ -88057,6 +90960,9 @@ "smithy.api#xmlName": "networkInsightsAnalysis" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#StartVpcEndpointServicePrivateDnsVerification": { @@ -88108,6 +91014,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#State": { @@ -88346,6 +91255,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#Storage": { @@ -89103,6 +92015,26 @@ } } }, + "com.amazonaws.ec2#SupportedAdditionalProcessorFeature": { + "type": "enum", + "members": { + "AMD_SEV_SNP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "amd-sev-snp" + } + } + } + }, + "com.amazonaws.ec2#SupportedAdditionalProcessorFeatureList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#SupportedAdditionalProcessorFeature", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#SupportedIpAddressTypes": { "type": "list", "member": { @@ -89687,6 +92619,9 @@ "smithy.api#xmlName": "connectionStatuses" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#TerminateConnectionStatus": { @@ -89780,6 +92715,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ThreadsPerCore": { @@ -93788,6 +96726,16 @@ "smithy.api#documentation": "

Options for logging VPN tunnel activity.

", "smithy.api#xmlName": "logOptions" } + }, + "EnableTunnelLifecycleControl": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "aws.protocols#ec2QueryName": "EnableTunnelLifecycleControl", + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Status of tunnel endpoint lifecycle control feature.

", + "smithy.api#xmlName": "enableTunnelLifecycleControl" + } } }, "traits": { @@ -93875,6 +96823,9 @@ "smithy.api#xmlName": "unassignedIpv6PrefixSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UnassignPrivateIpAddresses": { @@ -93995,6 +96946,9 @@ "smithy.api#xmlName": "natGatewayAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UnlimitedSupportedInstanceFamily": { @@ -94076,6 +97030,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UnsuccessfulInstanceCreditSpecificationErrorCode": { @@ -94296,6 +97253,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UpdateSecurityGroupRuleDescriptionsIngress": { @@ -94364,6 +97324,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UsageClassType": { @@ -95651,7 +98614,7 @@ "target": "com.amazonaws.ec2#OidcOptions", "traits": { "aws.protocols#ec2QueryName": "OidcOptions", - "smithy.api#documentation": "

The OpenID Connect details for an oidc-type, user-identity based trust provider.

", + "smithy.api#documentation": "

The options for an OpenID Connect-compatible user-identity trust provider.

", "smithy.api#xmlName": "oidcOptions" } }, @@ -95659,7 +98622,7 @@ "target": "com.amazonaws.ec2#DeviceOptions", "traits": { "aws.protocols#ec2QueryName": "DeviceOptions", - "smithy.api#documentation": "

The options for device-identity type trust provider.

", + "smithy.api#documentation": "

The options for device-identity trust provider.

", "smithy.api#xmlName": "deviceOptions" } }, @@ -98578,6 +101541,14 @@ "traits": { "smithy.api#documentation": "

Options for logging VPN tunnel activity.

" } + }, + "EnableTunnelLifecycleControl": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Turn on or off tunnel endpoint lifecycle control feature.

" + } } }, "traits": { @@ -98684,6 +101655,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ZoneIdStringList": { diff --git a/aws/sdk/aws-models/ecs.json b/aws/sdk/aws-models/ecs.json index b408252d21..51c17eda2f 100644 --- a/aws/sdk/aws-models/ecs.json +++ b/aws/sdk/aws-models/ecs.json @@ -632,9 +632,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -645,9 +645,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -658,9 +658,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -671,9 +671,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -684,9 +684,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -697,9 +697,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -710,9 +710,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -723,9 +723,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -736,9 +736,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -749,9 +749,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -762,9 +762,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -775,9 +775,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -788,9 +788,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -801,9 +801,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -814,9 +814,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -827,9 +827,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -840,9 +840,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -853,9 +853,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -866,9 +866,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -879,9 +879,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -892,9 +892,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -905,9 +905,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": true, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -918,9 +918,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -931,9 +931,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": true, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -944,9 +944,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -957,9 +957,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": true, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -970,9 +970,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -983,9 +983,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -996,9 +996,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -1009,9 +1009,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": false } }, { @@ -1022,9 +1022,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -1035,9 +1035,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -1048,9 +1048,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -1061,9 +1061,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -1074,9 +1074,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -1087,9 +1087,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": false, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -1100,9 +1100,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": true, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -1113,9 +1113,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -1126,9 +1126,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -1139,9 +1139,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -1152,9 +1152,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", "UseFIPS": false, - "Region": "us-iso-west-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1165,9 +1176,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1178,9 +1200,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1191,9 +1224,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1204,9 +1248,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1218,8 +1262,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1229,9 +1273,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1241,11 +1285,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -1434,7 +1484,7 @@ "managedTerminationProtection": { "target": "com.amazonaws.ecs#ManagedTerminationProtection", "traits": { - "smithy.api#documentation": "

The managed termination protection setting to use for the Auto Scaling group capacity\n\t\t\tprovider. This determines whether the Auto Scaling group has managed termination\n\t\t\tprotection. The default is off.

\n \n

When using managed termination protection, managed scaling must also be used\n\t\t\t\totherwise managed termination protection doesn't work.

\n
\n

When managed termination protection is on, Amazon ECS prevents the Amazon EC2 instances in an Auto\n\t\t\tScaling group that contain tasks from being terminated during a scale-in action. The\n\t\t\tAuto Scaling group and each instance in the Auto Scaling group must have instance\n\t\t\tprotection from scale-in actions enabled as well. For more information, see Instance Protection in the Auto Scaling User Guide.

\n

When managed termination protection is off, your Amazon EC2 instances aren't protected from\n\t\t\ttermination when the Auto Scaling group scales in.

" + "smithy.api#documentation": "

The managed termination protection setting to use for the Auto Scaling group capacity\n\t\t\tprovider. This determines whether the Auto Scaling group has managed termination\n\t\t\tprotection. The default is off.

\n \n

When using managed termination protection, managed scaling must also be used\n\t\t\t\totherwise managed termination protection doesn't work.

\n
\n

When managed termination protection is on, Amazon ECS prevents the Amazon EC2 instances in an Auto\n\t\t\tScaling group that contain tasks from being terminated during a scale-in action. The\n\t\t\tAuto Scaling group and each instance in the Auto Scaling group must have instance\n\t\t\tprotection from scale-in actions on as well. For more information, see Instance Protection in the Auto Scaling User Guide.

\n

When managed termination protection is off, your Amazon EC2 instances aren't protected from\n\t\t\ttermination when the Auto Scaling group scales in.

" } } }, @@ -1959,7 +2009,7 @@ "namespace": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The namespace name or full Amazon Resource Name (ARN) of the Cloud Map namespace that's used when you create a service and don't specify\n\t\t\ta Service Connect configuration. The namespace name can include up to 1024 characters.\n\t\t\tThe name is case-sensitive. The name can't include hyphens (-), tilde (~), greater than\n\t\t\t(>), less than (<), or slash (/).

\n

If you enter an existing namespace name or ARN, then that namespace will be used.\n\t\t\tAny namespace type is supported. The namespace must be in this account and this Amazon Web Services\n\t\t\tRegion.

\n

If you enter a new name, a Cloud Map namespace will be created. Amazon ECS creates a\n\t\t\tCloud Map namespace with the \"API calls\" method of instance discovery only. This instance\n\t\t\tdiscovery method is the \"HTTP\" namespace type in the Command Line Interface. Other types of instance\n\t\t\tdiscovery aren't used by Service Connect.

\n

If you update the service with an empty string \"\" for the namespace name,\n\t\t\tthe cluster configuration for Service Connect is removed. Note that the namespace will\n\t\t\tremain in Cloud Map and must be deleted separately.

\n

For more information about Cloud Map, see Working\n\t\t\t\twith Services in the Cloud Map Developer Guide.

", + "smithy.api#documentation": "

The namespace name or full Amazon Resource Name (ARN) of the Cloud Map namespace that's used when you create a service and don't specify\n\t\t\ta Service Connect configuration. The namespace name can include up to 1024 characters.\n\t\t\tThe name is case-sensitive. The name can't include hyphens (-), tilde (~), greater than\n\t\t\t(>), less than (<), or slash (/).

\n

If you enter an existing namespace name or ARN, then that namespace will be used.\n\t\t\tAny namespace type is supported. The namespace must be in this account and this Amazon Web Services\n\t\t\tRegion.

\n

If you enter a new name, a Cloud Map namespace will be created. Amazon ECS creates a\n\t\t\tCloud Map namespace with the \"API calls\" method of instance discovery only. This instance\n\t\t\tdiscovery method is the \"HTTP\" namespace type in the Command Line Interface. Other types of instance\n\t\t\tdiscovery aren't used by Service Connect.

\n

If you update the service with an empty string \"\" for the namespace name,\n\t\t\tthe cluster configuration for Service Connect is removed. Note that the namespace will\n\t\t\tremain in Cloud Map and must be deleted separately.

\n

For more information about Cloud Map, see Working\n\t\t\t\twith Services in the Cloud Map Developer Guide.

", "smithy.api#required": {} } } @@ -1974,13 +2024,13 @@ "name": { "target": "com.amazonaws.ecs#ClusterSettingName", "traits": { - "smithy.api#documentation": "

The name of the cluster setting. The only supported value is\n\t\t\t\tcontainerInsights.

" + "smithy.api#documentation": "

The name of the cluster setting. The value is containerInsights .

" } }, "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The value to set for the cluster setting. The supported values are enabled and\n\t\t\t\tdisabled. If enabled is specified, CloudWatch Container Insights\n\t\t\twill be enabled for the cluster, otherwise it will be off unless the\n\t\t\t\tcontainerInsights account setting is turned on. If a cluster value is\n\t\t\tspecified, it will override the containerInsights value set with PutAccountSetting or PutAccountSettingDefault.

" + "smithy.api#documentation": "

The value to set for the cluster setting. The supported values are enabled and\n\t\t\t\tdisabled.

\n

If you set name to containerInsights and value\n\t\t\tto enabled, CloudWatch Container Insights will be on for the cluster, otherwise\n\t\t\tit will be off unless the containerInsights account setting is turned on.\n\t\t\tIf a cluster value is specified, it will override the containerInsights\n\t\t\tvalue set with PutAccountSetting or PutAccountSettingDefault.

" } } }, @@ -2087,7 +2137,7 @@ "imageDigest": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The container image manifest digest.

\n \n

The imageDigest is only returned if the container is using an image\n\t\t\t\thosted in Amazon ECR, otherwise it is omitted.

\n
" + "smithy.api#documentation": "

The container image manifest digest.

" } }, "runtimeId": { @@ -2377,7 +2427,7 @@ "dockerSecurityOptions": { "target": "com.amazonaws.ecs#StringList", "traits": { - "smithy.api#documentation": "

A list of strings to provide custom labels for SELinux and AppArmor multi-level\n\t\t\tsecurity systems. This field isn't valid for containers in tasks using the\n\t\t\tFargate launch type.

\n

With Windows containers, this parameter can be used to reference a credential spec\n\t\t\tfile when configuring a container for Active Directory authentication. For more\n\t\t\tinformation, see Using gMSAs for Windows\n\t\t\t\tContainers in the Amazon Elastic Container Service Developer Guide.

\n

This parameter maps to SecurityOpt in the\n\t\t\tCreate a container section of the Docker Remote API and the\n\t\t\t\t--security-opt option to docker\n\t\t\t\trun.

\n \n

The Amazon ECS container agent running on a container instance must register with the\n\t\t\t\t\tECS_SELINUX_CAPABLE=true or ECS_APPARMOR_CAPABLE=true\n\t\t\t\tenvironment variables before containers placed on that instance can use these\n\t\t\t\tsecurity options. For more information, see Amazon ECS Container\n\t\t\t\t\tAgent Configuration in the Amazon Elastic Container Service Developer Guide.

\n
\n

For more information about valid values, see Docker\n\t\t\t\tRun Security Configuration.

\n

Valid values: \"no-new-privileges\" | \"apparmor:PROFILE\" | \"label:value\" |\n\t\t\t\"credentialspec:CredentialSpecFilePath\"

" + "smithy.api#documentation": "

A list of strings to provide custom configuration for multiple\n\t\t\tsecurity systems. For more information about valid values, see Docker Run Security Configuration. This field isn't valid\n\t\t\tfor containers in tasks using the Fargate launch\n\t\t\ttype.

\n

For Linux tasks on EC2, this parameter can be used to reference custom\n\t\t\tlabels for SELinux and AppArmor multi-level security systems.

\n

For any tasks on EC2, this parameter can be used to reference a\n\t\t\tcredential spec file that configures a container for Active Directory\n\t\t\tauthentication. For more\n\t\t\tinformation, see Using gMSAs for Windows\n\t\t\t\tContainers and Using gMSAs for Linux\n\t\t\t\t\tContainers in the Amazon Elastic Container Service Developer Guide.

\n

This parameter maps to SecurityOpt in the\n\t\t\tCreate a container section of the Docker Remote API and the\n\t\t\t\t--security-opt option to docker\n\t\t\t\trun.

\n \n

The Amazon ECS container agent running on a container instance must register with the\n\t\t\t\t\tECS_SELINUX_CAPABLE=true or ECS_APPARMOR_CAPABLE=true\n\t\t\t\tenvironment variables before containers placed on that instance can use these\n\t\t\t\tsecurity options. For more information, see Amazon ECS Container\n\t\t\t\t\tAgent Configuration in the Amazon Elastic Container Service Developer Guide.

\n
\n

For more information about valid values, see Docker\n\t\t\t\tRun Security Configuration.

\n

Valid values: \"no-new-privileges\" | \"apparmor:PROFILE\" | \"label:value\" |\n\t\t\t\"credentialspec:CredentialSpecFilePath\"

" } }, "interactive": { @@ -2887,6 +2937,9 @@ { "target": "com.amazonaws.ecs#InvalidParameterException" }, + { + "target": "com.amazonaws.ecs#NamespaceNotFoundException" + }, { "target": "com.amazonaws.ecs#ServerException" } @@ -2997,7 +3050,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs and maintains your desired number of tasks from a specified task definition. If\n\t\t\tthe number of tasks running in a service drops below the desiredCount,\n\t\t\tAmazon ECS runs another copy of the task in the specified cluster. To update an existing\n\t\t\tservice, see the UpdateService action.

\n

In addition to maintaining the desired count of tasks in your service, you can\n\t\t\toptionally run your service behind one or more load balancers. The load balancers\n\t\t\tdistribute traffic across the tasks that are associated with the service. For more\n\t\t\tinformation, see Service load balancing in the Amazon Elastic Container Service Developer Guide.

\n

Tasks for services that don't use a load balancer are considered healthy if they're in\n\t\t\tthe RUNNING state. Tasks for services that use a load balancer are\n\t\t\tconsidered healthy if they're in the RUNNING state and are reported as\n\t\t\thealthy by the load balancer.

\n

There are two service scheduler strategies available:

\n
    \n
  • \n

    \n REPLICA - The replica scheduling strategy places and\n\t\t\t\t\tmaintains your desired number of tasks across your cluster. By default, the\n\t\t\t\t\tservice scheduler spreads tasks across Availability Zones. You can use task\n\t\t\t\t\tplacement strategies and constraints to customize task placement decisions. For\n\t\t\t\t\tmore information, see Service scheduler concepts in the Amazon Elastic Container Service Developer Guide.

    \n
  • \n
  • \n

    \n DAEMON - The daemon scheduling strategy deploys exactly one\n\t\t\t\t\ttask on each active container instance that meets all of the task placement\n\t\t\t\t\tconstraints that you specify in your cluster. The service scheduler also\n\t\t\t\t\tevaluates the task placement constraints for running tasks. It also stops tasks\n\t\t\t\t\tthat don't meet the placement constraints. When using this strategy, you don't\n\t\t\t\t\tneed to specify a desired number of tasks, a task placement strategy, or use\n\t\t\t\t\tService Auto Scaling policies. For more information, see Service scheduler concepts in the Amazon Elastic Container Service Developer Guide.

    \n
  • \n
\n

You can optionally specify a deployment configuration for your service. The deployment\n\t\t\tis initiated by changing properties. For example, the deployment might be initiated by\n\t\t\tthe task definition or by your desired count of a service. This is done with an UpdateService operation. The default value for a replica service for\n\t\t\t\tminimumHealthyPercent is 100%. The default value for a daemon service\n\t\t\tfor minimumHealthyPercent is 0%.

\n

If a service uses the ECS deployment controller, the minimum healthy\n\t\t\tpercent represents a lower limit on the number of tasks in a service that must remain in\n\t\t\tthe RUNNING state during a deployment. Specifically, it represents it as a\n\t\t\tpercentage of your desired number of tasks (rounded up to the nearest integer). This\n\t\t\thappens when any of your container instances are in the DRAINING state if\n\t\t\tthe service contains tasks using the EC2 launch type. Using this\n\t\t\tparameter, you can deploy without using additional cluster capacity. For example, if you\n\t\t\tset your service to have desired number of four tasks and a minimum healthy percent of\n\t\t\t50%, the scheduler might stop two existing tasks to free up cluster capacity before\n\t\t\tstarting two new tasks. If they're in the RUNNING state, tasks for services\n\t\t\tthat don't use a load balancer are considered healthy . If they're in the\n\t\t\t\tRUNNING state and reported as healthy by the load balancer, tasks for\n\t\t\tservices that do use a load balancer are considered healthy . The\n\t\t\tdefault value for minimum healthy percent is 100%.

\n

If a service uses the ECS deployment controller, the maximum percent parameter represents an upper limit on the\n\t\t\tnumber of tasks in a service that are allowed in the RUNNING or\n\t\t\t\tPENDING state during a deployment. Specifically, it represents it as a\n\t\t\tpercentage of the desired number of tasks (rounded down to the nearest integer). This\n\t\t\thappens when any of your container instances are in the DRAINING state if\n\t\t\tthe service contains tasks using the EC2 launch type. Using this\n\t\t\tparameter, you can define the deployment batch size. For example, if your service has a\n\t\t\tdesired number of four tasks and a maximum percent value of 200%, the scheduler may\n\t\t\tstart four new tasks before stopping the four older tasks (provided that the cluster\n\t\t\tresources required to do this are available). The default value for maximum percent is\n\t\t\t200%.

\n

If a service uses either the CODE_DEPLOY or EXTERNAL\n\t\t\tdeployment controller types and tasks that use the EC2 launch type, the\n\t\t\t\tminimum healthy percent and maximum percent values are used only to define the lower and upper limit\n\t\t\ton the number of the tasks in the service that remain in the RUNNING state.\n\t\t\tThis is while the container instances are in the DRAINING state. If the\n\t\t\ttasks in the service use the Fargate launch type, the minimum healthy\n\t\t\tpercent and maximum percent values aren't used. This is the case even if they're\n\t\t\tcurrently visible when describing your service.

\n

When creating a service that uses the EXTERNAL deployment controller, you\n\t\t\tcan specify only parameters that aren't controlled at the task set level. The only\n\t\t\trequired parameter is the service name. You control your services using the CreateTaskSet operation. For more information, see Amazon ECS deployment types in the Amazon Elastic Container Service Developer Guide.

\n

When the service scheduler launches new tasks, it determines task placement. For\n\t\t\tinformation about task placement and task placement strategies, see Amazon ECS\n\t\t\t\ttask placement in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Runs and maintains your desired number of tasks from a specified task definition. If\n\t\t\tthe number of tasks running in a service drops below the desiredCount,\n\t\t\tAmazon ECS runs another copy of the task in the specified cluster. To update an existing\n\t\t\tservice, see the UpdateService action.

\n \n

Starting April 15, 2023, Amazon Web Services will not onboard new customers to Amazon Elastic Inference (EI), and will help current customers migrate their workloads to options that offer better price and performance. After April 15, 2023, new customers will not be able to launch instances with Amazon EI accelerators in Amazon SageMaker, Amazon ECS, or Amazon EC2. However, customers who have used Amazon EI at least once during the past 30-day period are considered current customers and will be able to continue using the service.

\n
\n

In addition to maintaining the desired count of tasks in your service, you can\n\t\t\toptionally run your service behind one or more load balancers. The load balancers\n\t\t\tdistribute traffic across the tasks that are associated with the service. For more\n\t\t\tinformation, see Service load balancing in the Amazon Elastic Container Service Developer Guide.

\n

Tasks for services that don't use a load balancer are considered healthy if they're in\n\t\t\tthe RUNNING state. Tasks for services that use a load balancer are\n\t\t\tconsidered healthy if they're in the RUNNING state and are reported as\n\t\t\thealthy by the load balancer.

\n

There are two service scheduler strategies available:

\n
    \n
  • \n

    \n REPLICA - The replica scheduling strategy places and\n\t\t\t\t\tmaintains your desired number of tasks across your cluster. By default, the\n\t\t\t\t\tservice scheduler spreads tasks across Availability Zones. You can use task\n\t\t\t\t\tplacement strategies and constraints to customize task placement decisions. For\n\t\t\t\t\tmore information, see Service scheduler concepts in the Amazon Elastic Container Service Developer Guide.

    \n
  • \n
  • \n

    \n DAEMON - The daemon scheduling strategy deploys exactly one\n\t\t\t\t\ttask on each active container instance that meets all of the task placement\n\t\t\t\t\tconstraints that you specify in your cluster. The service scheduler also\n\t\t\t\t\tevaluates the task placement constraints for running tasks. It also stops tasks\n\t\t\t\t\tthat don't meet the placement constraints. When using this strategy, you don't\n\t\t\t\t\tneed to specify a desired number of tasks, a task placement strategy, or use\n\t\t\t\t\tService Auto Scaling policies. For more information, see Service scheduler concepts in the Amazon Elastic Container Service Developer Guide.

    \n
  • \n
\n

You can optionally specify a deployment configuration for your service. The deployment\n\t\t\tis initiated by changing properties. For example, the deployment might be initiated by\n\t\t\tthe task definition or by your desired count of a service. This is done with an UpdateService operation. The default value for a replica service for\n\t\t\t\tminimumHealthyPercent is 100%. The default value for a daemon service\n\t\t\tfor minimumHealthyPercent is 0%.

\n

If a service uses the ECS deployment controller, the minimum healthy\n\t\t\tpercent represents a lower limit on the number of tasks in a service that must remain in\n\t\t\tthe RUNNING state during a deployment. Specifically, it represents it as a\n\t\t\tpercentage of your desired number of tasks (rounded up to the nearest integer). This\n\t\t\thappens when any of your container instances are in the DRAINING state if\n\t\t\tthe service contains tasks using the EC2 launch type. Using this\n\t\t\tparameter, you can deploy without using additional cluster capacity. For example, if you\n\t\t\tset your service to have desired number of four tasks and a minimum healthy percent of\n\t\t\t50%, the scheduler might stop two existing tasks to free up cluster capacity before\n\t\t\tstarting two new tasks. If they're in the RUNNING state, tasks for services\n\t\t\tthat don't use a load balancer are considered healthy . If they're in the\n\t\t\t\tRUNNING state and reported as healthy by the load balancer, tasks for\n\t\t\tservices that do use a load balancer are considered healthy . The\n\t\t\tdefault value for minimum healthy percent is 100%.

\n

If a service uses the ECS deployment controller, the maximum percent parameter represents an upper limit on the\n\t\t\tnumber of tasks in a service that are allowed in the RUNNING or\n\t\t\t\tPENDING state during a deployment. Specifically, it represents it as a\n\t\t\tpercentage of the desired number of tasks (rounded down to the nearest integer). This\n\t\t\thappens when any of your container instances are in the DRAINING state if\n\t\t\tthe service contains tasks using the EC2 launch type. Using this\n\t\t\tparameter, you can define the deployment batch size. For example, if your service has a\n\t\t\tdesired number of four tasks and a maximum percent value of 200%, the scheduler may\n\t\t\tstart four new tasks before stopping the four older tasks (provided that the cluster\n\t\t\tresources required to do this are available). The default value for maximum percent is\n\t\t\t200%.

\n

If a service uses either the CODE_DEPLOY or EXTERNAL\n\t\t\tdeployment controller types and tasks that use the EC2 launch type, the\n\t\t\t\tminimum healthy percent and maximum percent values are used only to define the lower and upper limit\n\t\t\ton the number of the tasks in the service that remain in the RUNNING state.\n\t\t\tThis is while the container instances are in the DRAINING state. If the\n\t\t\ttasks in the service use the Fargate launch type, the minimum healthy\n\t\t\tpercent and maximum percent values aren't used. This is the case even if they're\n\t\t\tcurrently visible when describing your service.

\n

When creating a service that uses the EXTERNAL deployment controller, you\n\t\t\tcan specify only parameters that aren't controlled at the task set level. The only\n\t\t\trequired parameter is the service name. You control your services using the CreateTaskSet operation. For more information, see Amazon ECS deployment types in the Amazon Elastic Container Service Developer Guide.

\n

When the service scheduler launches new tasks, it determines task placement. For\n\t\t\tinformation about task placement and task placement strategies, see Amazon ECS\n\t\t\t\ttask placement in the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#CreateServiceRequest": { @@ -3019,7 +3072,7 @@ "taskDefinition": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The family and revision (family:revision) or\n\t\t\tfull ARN of the task definition to run in your service. If a revision\n\t\t\tisn't specified, the latest ACTIVE revision is used.

\n

A task definition must be specified if the service uses either the ECS or\n\t\t\t\tCODE_DEPLOY deployment controllers.

" + "smithy.api#documentation": "

The family and revision (family:revision) or\n\t\t\tfull ARN of the task definition to run in your service. If a revision\n\t\t\tisn't specified, the latest ACTIVE revision is used.

\n

A task definition must be specified if the service uses either the ECS or\n\t\t\t\tCODE_DEPLOY deployment controllers.

\n

For more information about deployment types, see Amazon ECS deployment types.

" } }, "loadBalancers": { @@ -3037,7 +3090,7 @@ "desiredCount": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

The number of instantiations of the specified task definition to place and keep\n\t\t\trunning on your cluster.

\n

This is required if schedulingStrategy is REPLICA or isn't\n\t\t\tspecified. If schedulingStrategy is DAEMON then this isn't\n\t\t\trequired.

" + "smithy.api#documentation": "

The number of instantiations of the specified task definition to place and keep running in your service.

\n

This is required if schedulingStrategy is REPLICA or isn't\n\t\t\tspecified. If schedulingStrategy is DAEMON then this isn't\n\t\t\trequired.

" } }, "clientToken": { @@ -3128,14 +3181,14 @@ "propagateTags": { "target": "com.amazonaws.ecs#PropagateTags", "traits": { - "smithy.api#documentation": "

Specifies whether to propagate the tags from the task definition to the task. If no\n\t\t\tvalue is specified, the tags aren't propagated. Tags can only be propagated to the task\n\t\t\tduring task creation. To add tags to a task after task creation, use the TagResource API action.

" + "smithy.api#documentation": "

Specifies whether to propagate the tags from the task definition to the task. If no\n\t\t\tvalue is specified, the tags aren't propagated. Tags can only be propagated to the task\n\t\t\tduring task creation. To add tags to a task after task creation, use the TagResource API action.

" } }, "enableExecuteCommand": { "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Determines whether the execute command functionality is enabled for the service. If\n\t\t\t\ttrue, this enables execute command functionality on all containers in\n\t\t\tthe service tasks.

" + "smithy.api#documentation": "

Determines whether the execute command functionality is turned on for the service. If\n\t\t\t\ttrue, this enables execute command functionality on all containers in\n\t\t\tthe service tasks.

" } }, "serviceConnectConfiguration": { @@ -3863,7 +3916,7 @@ "rolloutState": { "target": "com.amazonaws.ecs#DeploymentRolloutState", "traits": { - "smithy.api#documentation": "\n

The rolloutState of a service is only returned for services that use\n\t\t\t\tthe rolling update (ECS) deployment type that aren't behind a\n\t\t\t\tClassic Load Balancer.

\n
\n

The rollout state of the deployment. When a service deployment is started, it begins\n\t\t\tin an IN_PROGRESS state. When the service reaches a steady state, the\n\t\t\tdeployment transitions to a COMPLETED state. If the service fails to reach\n\t\t\ta steady state and circuit breaker is enabled, the deployment transitions to a\n\t\t\t\tFAILED state. A deployment in FAILED state doesn't launch\n\t\t\tany new tasks. For more information, see DeploymentCircuitBreaker.

" + "smithy.api#documentation": "\n

The rolloutState of a service is only returned for services that use\n\t\t\t\tthe rolling update (ECS) deployment type that aren't behind a\n\t\t\t\tClassic Load Balancer.

\n
\n

The rollout state of the deployment. When a service deployment is started, it begins\n\t\t\tin an IN_PROGRESS state. When the service reaches a steady state, the\n\t\t\tdeployment transitions to a COMPLETED state. If the service fails to reach\n\t\t\ta steady state and circuit breaker is turned on, the deployment transitions to a\n\t\t\t\tFAILED state. A deployment in FAILED state doesn't launch\n\t\t\tany new tasks. For more information, see DeploymentCircuitBreaker.

" } }, "rolloutStateReason": { @@ -3941,7 +3994,7 @@ } }, "traits": { - "smithy.api#documentation": "\n

The deployment circuit breaker can only be used for services using the rolling\n\t\t\t\tupdate (ECS) deployment type.

\n
\n

The deployment circuit breaker determines whether a\n\t\t\tservice deployment will fail if the service can't reach a steady state. If enabled, a\n\t\t\tservice deployment will transition to a failed state and stop launching new tasks. You\n\t\t\tcan also configure Amazon ECS to roll back your service to the last completed deployment\n\t\t\tafter a failure. For more information, see Rolling\n\t\t\t\tupdate in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "\n

The deployment circuit breaker can only be used for services using the rolling\n\t\t\t\tupdate (ECS) deployment type.

\n
\n

The deployment circuit breaker determines whether a\n\t\t\tservice deployment will fail if the service can't reach a steady state. If it is turned on, a\n\t\t\tservice deployment will transition to a failed state and stop launching new tasks. You\n\t\t\tcan also configure Amazon ECS to roll back your service to the last completed deployment\n\t\t\tafter a failure. For more information, see Rolling\n\t\t\t\tupdate in the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#DeploymentConfiguration": { @@ -5015,13 +5068,13 @@ "accessPointId": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The Amazon EFS access point ID to use. If an access point is specified, the root directory\n\t\t\tvalue specified in the EFSVolumeConfiguration must either be omitted or set\n\t\t\tto / which will enforce the path set on the EFS access point. If an access\n\t\t\tpoint is used, transit encryption must be enabled in the\n\t\t\t\tEFSVolumeConfiguration. For more information, see Working with Amazon\n\t\t\t\tEFS access points in the Amazon Elastic File System User Guide.

" + "smithy.api#documentation": "

The Amazon EFS access point ID to use. If an access point is specified, the root directory\n\t\t\tvalue specified in the EFSVolumeConfiguration must either be omitted or set\n\t\t\tto / which will enforce the path set on the EFS access point. If an access\n\t\t\tpoint is used, transit encryption must be on in the\n\t\t\t\tEFSVolumeConfiguration. For more information, see Working with Amazon\n\t\t\t\tEFS access points in the Amazon Elastic File System User Guide.

" } }, "iam": { "target": "com.amazonaws.ecs#EFSAuthorizationConfigIAM", "traits": { - "smithy.api#documentation": "

Determines whether to use the Amazon ECS task role defined in a task definition when\n\t\t\tmounting the Amazon EFS file system. If enabled, transit encryption must be enabled in the\n\t\t\t\tEFSVolumeConfiguration. If this parameter is omitted, the default value\n\t\t\tof DISABLED is used. For more information, see Using\n\t\t\t\tAmazon EFS access points in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Determines whether to use the Amazon ECS task role defined in a task definition when\n\t\t\tmounting the Amazon EFS file system. If it is turned on, transit encryption must be turned on in the\n\t\t\t\tEFSVolumeConfiguration. If this parameter is omitted, the default value\n\t\t\tof DISABLED is used. For more information, see Using\n\t\t\t\tAmazon EFS access points in the Amazon Elastic Container Service Developer Guide.

" } } }, @@ -5082,7 +5135,7 @@ "transitEncryption": { "target": "com.amazonaws.ecs#EFSTransitEncryption", "traits": { - "smithy.api#documentation": "

Determines whether to use encryption for Amazon EFS data in transit between the Amazon ECS host\n\t\t\tand the Amazon EFS server. Transit encryption must be enabled if Amazon EFS IAM authorization is\n\t\t\tused. If this parameter is omitted, the default value of DISABLED is used.\n\t\t\tFor more information, see Encrypting data in transit in\n\t\t\tthe Amazon Elastic File System User Guide.

" + "smithy.api#documentation": "

Determines whether to use encryption for Amazon EFS data in transit between the Amazon ECS host\n\t\t\tand the Amazon EFS server. Transit encryption must be turned on if Amazon EFS IAM authorization is\n\t\t\tused. If this parameter is omitted, the default value of DISABLED is used.\n\t\t\tFor more information, see Encrypting data in transit in\n\t\t\tthe Amazon Elastic File System User Guide.

" } }, "transitEncryptionPort": { @@ -5160,7 +5213,7 @@ } }, "traits": { - "smithy.api#documentation": "

The amount of ephemeral storage to allocate for the task. This parameter is used to\n\t\t\texpand the total amount of ephemeral storage available, beyond the default amount, for\n\t\t\ttasks hosted on Fargate. For more information, see Fargate task\n\t\t\t\tstorage in the Amazon ECS User Guide for Fargate.

\n \n

This parameter is only supported for tasks hosted on Fargate using\n\t\t\t\tLinux platform version 1.4.0 or later. This parameter is not supported\n\t\t\t\tfor Windows containers on Fargate.

\n
" + "smithy.api#documentation": "

The amount of ephemeral storage to allocate for the task. This parameter is used to\n\t\t\texpand the total amount of ephemeral storage available, beyond the default amount, for\n\t\t\ttasks hosted on Fargate. For more information, see Fargate task\n\t\t\t\tstorage in the Amazon ECS User Guide for Fargate.

\n \n

For tasks using the Fargate launch type, the task requires\n\t\t\t\tthe following platforms:

\n
    \n
  • \n

    Linux platform version 1.4.0 or later.

    \n
  • \n
  • \n

    Windows platform version 1.0.0 or later.

    \n
  • \n
\n
" } }, "com.amazonaws.ecs#ExecuteCommand": { @@ -5192,7 +5245,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs a command remotely on a container within a task.

\n

If you use a condition key in your IAM policy to refine the conditions for the policy\n\t\t\tstatement, for example limit the actions to a specific cluster, you receive an\n\t\t\t\tAccessDeniedException when there is a mismatch between the condition\n\t\t\tkey value and the corresponding parameter value.

\n

For information about required permissions and considerations, see Using Amazon ECS Exec for\n\t\t\tdebugging in the Amazon ECS Developer Guide.

" + "smithy.api#documentation": "

Runs a command remotely on a container within a task.

\n

If you use a condition key in your IAM policy to refine the conditions for the policy\n\t\t\tstatement, for example limit the actions to a specific cluster, you receive an\n\t\t\t\tAccessDeniedException when there is a mismatch between the condition\n\t\t\tkey value and the corresponding parameter value.

\n

For information about required permissions and considerations, see Using Amazon ECS Exec for\n\t\t\tdebugging in the Amazon ECS Developer Guide.

" } }, "com.amazonaws.ecs#ExecuteCommandConfiguration": { @@ -5563,7 +5616,7 @@ "protectedTasks": { "target": "com.amazonaws.ecs#ProtectedTasks", "traits": { - "smithy.api#documentation": "

A list of tasks with the following information.

\n
    \n
  • \n

    \n taskArn: The task ARN.

    \n
  • \n
  • \n

    \n protectionEnabled: The protection status of the task. If scale-in\n\t\t\t\t\tprotection is enabled for a task, the value is true. Otherwise, it\n\t\t\t\t\tis false.

    \n
  • \n
  • \n

    \n expirationDate: The epoch time when protection for the task will\n\t\t\t\t\texpire.

    \n
  • \n
" + "smithy.api#documentation": "

A list of tasks with the following information.

\n
    \n
  • \n

    \n taskArn: The task ARN.

    \n
  • \n
  • \n

    \n protectionEnabled: The protection status of the task. If scale-in\n\t\t\t\t\tprotection is turned on for a task, the value is true. Otherwise, it\n\t\t\t\t\tis false.

    \n
  • \n
  • \n

    \n expirationDate: The epoch time when protection for the task will\n\t\t\t\t\texpire.

    \n
  • \n
" } }, "failures": { @@ -5619,7 +5672,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object representing a container health check. Health check parameters that are\n\t\t\tspecified in a container definition override any Docker health checks that exist in the\n\t\t\tcontainer image (such as those specified in a parent image or from the image's\n\t\t\tDockerfile).

\n \n

The Amazon ECS container agent only monitors and reports on the health checks specified\n\t\t\t\tin the task definition. Amazon ECS does not monitor Docker health checks that are\n\t\t\t\tembedded in a container image and not specified in the container definition. Health\n\t\t\t\tcheck parameters that are specified in a container definition override any Docker\n\t\t\t\thealth checks that exist in the container image.

\n
\n

You can view the health status of both individual containers and a task with the\n\t\t\tDescribeTasks API operation or when viewing the task details in the console.

\n

The following describes the possible healthStatus values for a\n\t\t\tcontainer:

\n
    \n
  • \n

    \n HEALTHY-The container health check has passed\n\t\t\t\t\tsuccessfully.

    \n
  • \n
  • \n

    \n UNHEALTHY-The container health check has failed.

    \n
  • \n
  • \n

    \n UNKNOWN-The container health check is being evaluated or\n\t\t\t\t\tthere's no container health check defined.

    \n
  • \n
\n

The following describes the possible healthStatus values for a task. The\n\t\t\tcontainer health check status of nonessential containers only affects the health status\n\t\t\tof a task if no essential containers have health checks defined.

\n
    \n
  • \n

    \n HEALTHY-All essential containers within the task have\n\t\t\t\t\tpassed their health checks.

    \n
  • \n
  • \n

    \n UNHEALTHY-One or more essential containers have failed\n\t\t\t\t\ttheir health check.

    \n
  • \n
  • \n

    \n UNKNOWN-The essential containers within the task are still\n\t\t\t\t\thaving their health checks evaluated or there are only nonessential containers\n\t\t\t\t\twith health checks defined.

    \n
  • \n
\n

If a task is run manually, and not as part of a service, the task will continue its\n\t\t\tlifecycle regardless of its health status. For tasks that are part of a service, if the\n\t\t\ttask reports as unhealthy then the task will be stopped and the service scheduler will\n\t\t\treplace it.

\n \n

For tasks that are a part of a service and the service uses the ECS\n\t\t\t\trolling deployment type, the deployment is paused while the new tasks have the\n\t\t\t\t\tUNKNOWN task health check status. For example, tasks that define\n\t\t\t\thealth checks for nonessential containers when no essential containers have health\n\t\t\t\tchecks will have the UNKNOWN health check status indefinitely which\n\t\t\t\tprevents the deployment from completing.

\n
\n

The following are notes about container health check support:

\n
    \n
  • \n

    Container health checks require version 1.17.0 or greater of the Amazon ECS\n\t\t\t\t\tcontainer agent. For more information, see Updating the\n\t\t\t\t\t\tAmazon ECS container agent.

    \n
  • \n
  • \n

    Container health checks are supported for Fargate tasks if\n\t\t\t\t\tyou're using platform version 1.1.0 or greater. For more\n\t\t\t\t\tinformation, see Fargate\n\t\t\t\t\t\tplatform versions.

    \n
  • \n
  • \n

    Container health checks aren't supported for tasks that are part of a service\n\t\t\t\t\tthat's configured to use a Classic Load Balancer.

    \n
  • \n
" + "smithy.api#documentation": "

An object representing a container health check. Health check parameters that are\n\t\t\tspecified in a container definition override any Docker health checks that exist in the\n\t\t\tcontainer image (such as those specified in a parent image or from the image's\n\t\t\tDockerfile). This configuration maps to the HEALTHCHECK parameter of docker run.

\n \n

The Amazon ECS container agent only monitors and reports on the health checks specified\n\t\t\t\tin the task definition. Amazon ECS does not monitor Docker health checks that are\n\t\t\t\tembedded in a container image and not specified in the container definition. Health\n\t\t\t\tcheck parameters that are specified in a container definition override any Docker\n\t\t\t\thealth checks that exist in the container image.

\n
\n

You can view the health status of both individual containers and a task with the\n\t\t\tDescribeTasks API operation or when viewing the task details in the console.

\n

The following describes the possible healthStatus values for a\n\t\t\tcontainer:

\n
    \n
  • \n

    \n HEALTHY-The container health check has passed\n\t\t\t\t\tsuccessfully.

    \n
  • \n
  • \n

    \n UNHEALTHY-The container health check has failed.

    \n
  • \n
  • \n

    \n UNKNOWN-The container health check is being evaluated or\n\t\t\t\t\tthere's no container health check defined.

    \n
  • \n
\n

The following describes the possible healthStatus values for a task. The\n\t\t\tcontainer health check status of\n\t\t\tnon-essential containers don't have an effect on the health status of a task.

\n
    \n
  • \n

    \n HEALTHY-All essential containers within the task have\n\t\t\t\t\tpassed their health checks.

    \n
  • \n
  • \n

    \n UNHEALTHY-One or more essential containers have failed\n\t\t\t\t\ttheir health check.

    \n
  • \n
  • \n

    \n UNKNOWN-The essential containers within the task are still\n\t\t\t\t\thaving their health checks evaluated, there are only nonessential containers\n\t\t\t\t\twith health checks defined, or there are no container health checks\n\t\t\t\t\tdefined.

    \n
  • \n
\n

If a task is run manually, and not as part of a service, the task will continue its\n\t\t\tlifecycle regardless of its health status. For tasks that are part of a service, if the\n\t\t\ttask reports as unhealthy then the task will be stopped and the service scheduler will\n\t\t\treplace it.

\n

The following are notes about container health check support:

\n
    \n
  • \n

    Container health checks require version 1.17.0 or greater of the Amazon ECS\n\t\t\t\t\tcontainer agent. For more information, see Updating the\n\t\t\t\t\t\tAmazon ECS container agent.

    \n
  • \n
  • \n

    Container health checks are supported for Fargate tasks if\n\t\t\t\t\tyou're using platform version 1.1.0 or greater. For more\n\t\t\t\t\tinformation, see Fargate\n\t\t\t\t\t\tplatform versions.

    \n
  • \n
  • \n

    Container health checks aren't supported for tasks that are part of a service\n\t\t\t\t\tthat's configured to use a Classic Load Balancer.

    \n
  • \n
" } }, "com.amazonaws.ecs#HealthStatus": { @@ -5693,7 +5746,7 @@ "deviceName": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The Elastic Inference accelerator device name. The deviceName must also\n\t\t\tbe referenced in a container definition as a ResourceRequirement.

", + "smithy.api#documentation": "

The Elastic Inference accelerator device name. The deviceName must also\n\t\t\tbe referenced in a container definition as a ResourceRequirement.

", "smithy.api#required": {} } }, @@ -5971,18 +6024,18 @@ "maxSwap": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

The total amount of swap memory (in MiB) a container can use. This parameter will be\n\t\t\ttranslated to the --memory-swap option to docker run where the value would be the sum of\n\t\t\tthe container memory plus the maxSwap value.

\n

If a maxSwap value of 0 is specified, the container will not\n\t\t\tuse swap. Accepted values are 0 or any positive integer. If the\n\t\t\t\tmaxSwap parameter is omitted, the container will use the swap\n\t\t\tconfiguration for the container instance it is running on. A maxSwap value\n\t\t\tmust be set for the swappiness parameter to be used.

\n \n

If you're using tasks that use the Fargate launch type, the\n\t\t\t\t\tmaxSwap parameter isn't supported.

\n
" + "smithy.api#documentation": "

The total amount of swap memory (in MiB) a container can use. This parameter will be\n\t\t\ttranslated to the --memory-swap option to docker run where the value would be the sum of\n\t\t\tthe container memory plus the maxSwap value.

\n

If a maxSwap value of 0 is specified, the container will not\n\t\t\tuse swap. Accepted values are 0 or any positive integer. If the\n\t\t\t\tmaxSwap parameter is omitted, the container will use the swap\n\t\t\tconfiguration for the container instance it is running on. A maxSwap value\n\t\t\tmust be set for the swappiness parameter to be used.

\n \n

If you're using tasks that use the Fargate launch type, the\n\t\t\t\t\tmaxSwap parameter isn't supported.

\n

If you're using tasks on Amazon Linux 2023 the swappiness parameter isn't supported.

\n
" } }, "swappiness": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

This allows you to tune a container's memory swappiness behavior. A\n\t\t\t\tswappiness value of 0 will cause swapping to not happen\n\t\t\tunless absolutely necessary. A swappiness value of 100 will\n\t\t\tcause pages to be swapped very aggressively. Accepted values are whole numbers between\n\t\t\t\t0 and 100. If the swappiness parameter is not\n\t\t\tspecified, a default value of 60 is used. If a value is not specified for\n\t\t\t\tmaxSwap then this parameter is ignored. This parameter maps to the\n\t\t\t\t--memory-swappiness option to docker run.

\n \n

If you're using tasks that use the Fargate launch type, the\n\t\t\t\t\tswappiness parameter isn't supported.

\n
" + "smithy.api#documentation": "

This allows you to tune a container's memory swappiness behavior. A\n\t\t\t\tswappiness value of 0 will cause swapping to not happen\n\t\t\tunless absolutely necessary. A swappiness value of 100 will\n\t\t\tcause pages to be swapped very aggressively. Accepted values are whole numbers between\n\t\t\t\t0 and 100. If the swappiness parameter is not\n\t\t\tspecified, a default value of 60 is used. If a value is not specified for\n\t\t\t\tmaxSwap then this parameter is ignored. This parameter maps to the\n\t\t\t\t--memory-swappiness option to docker run.

\n \n

If you're using tasks that use the Fargate launch type, the\n\t\t\t\t\tswappiness parameter isn't supported.

\n

If you're using tasks on Amazon Linux 2023 the swappiness parameter isn't supported.

\n
" } } }, "traits": { - "smithy.api#documentation": "

Linux-specific options that are applied to the container, such as Linux KernelCapabilities.

" + "smithy.api#documentation": "

The Linux-specific options that are applied to the container, such as Linux KernelCapabilities.

" } }, "com.amazonaws.ecs#ListAccountSettings": { @@ -6868,7 +6921,7 @@ } }, "traits": { - "smithy.api#documentation": "

The load balancer configuration to use with a service or task set.

\n

For specific notes and restrictions regarding the use of load balancers with services\n\t\t\tand task sets, see the CreateService and CreateTaskSet actions.

\n

When you add, update, or remove a load balancer configuration, Amazon ECS starts a new\n\t\t\tdeployment with the updated Elastic Load Balancing configuration. This causes tasks to register to and\n\t\t\tderegister from load balancers.

\n

We recommend that you verify this on a test environment before you update the Elastic Load Balancing\n\t\t\tconfiguration.

\n

A service-linked role is required for services that use multiple target groups. For\n\t\t\tmore information, see Using\n\t\t\t\tservice-linked roles in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

The load balancer configuration to use with a service or task set.

\n

When you add, update, or remove a load balancer configuration, Amazon ECS starts a new\n\t\t\tdeployment with the updated Elastic Load Balancing configuration. This causes tasks to register to and\n\t\t\tderegister from load balancers.

\n

We recommend that you verify this on a test environment before you update the Elastic Load Balancing\n\t\t\tconfiguration.

\n

A service-linked role is required for services that use multiple target groups. For\n\t\t\tmore information, see Using\n\t\t\t\tservice-linked roles in the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#LoadBalancers": { @@ -6984,7 +7037,7 @@ "name": { "target": "com.amazonaws.ecs#ManagedAgentName", "traits": { - "smithy.api#documentation": "

The name of the managed agent. When the execute command feature is enabled, the\n\t\t\tmanaged agent name is ExecuteCommandAgent.

" + "smithy.api#documentation": "

The name of the managed agent. When the execute command feature is turned on, the\n\t\t\tmanaged agent name is ExecuteCommandAgent.

" } }, "reason": { @@ -7074,7 +7127,7 @@ "targetCapacity": { "target": "com.amazonaws.ecs#ManagedScalingTargetCapacity", "traits": { - "smithy.api#documentation": "

The target capacity value for the capacity provider. The specified value must be\n\t\t\tgreater than 0 and less than or equal to 100. A value of\n\t\t\t\t100 results in the Amazon EC2 instances in your Auto Scaling group being\n\t\t\tcompletely used.

" + "smithy.api#documentation": "

The target capacity utilization as a percentage for the capacity provider. The\n\t\t\tspecified value must be greater than 0 and less than or equal to\n\t\t\t\t100. For example, if you want the capacity provider to maintain 10%\n\t\t\tspare capacity, then that means the utilization is 90%, so use a\n\t\t\t\ttargetCapacity of 90. The default value of\n\t\t\t\t100 percent results in the Amazon EC2 instances in your Auto Scaling group being\n\t\t\tcompletely used.

" } }, "minimumScalingStepSize": { @@ -7097,7 +7150,7 @@ } }, "traits": { - "smithy.api#documentation": "

The managed scaling settings for the Auto Scaling group capacity provider.

\n

When managed scaling is enabled, Amazon ECS manages the scale-in and scale-out actions of\n\t\t\tthe Auto Scaling group. Amazon ECS manages a target tracking scaling policy using an Amazon ECS\n\t\t\tmanaged CloudWatch metric with the specified targetCapacity value as the target\n\t\t\tvalue for the metric. For more information, see Using managed scaling in the Amazon Elastic Container Service Developer Guide.

\n

If managed scaling is off, the user must manage the scaling of the Auto Scaling\n\t\t\tgroup.

" + "smithy.api#documentation": "

The managed scaling settings for the Auto Scaling group capacity provider.

\n

When managed scaling is turned on, Amazon ECS manages the scale-in and scale-out actions of\n\t\t\tthe Auto Scaling group. Amazon ECS manages a target tracking scaling policy using an Amazon ECS\n\t\t\tmanaged CloudWatch metric with the specified targetCapacity value as the target\n\t\t\tvalue for the metric. For more information, see Using managed scaling in the Amazon Elastic Container Service Developer Guide.

\n

If managed scaling is off, the user must manage the scaling of the Auto Scaling\n\t\t\tgroup.

" } }, "com.amazonaws.ecs#ManagedScalingInstanceWarmupPeriod": { @@ -7196,7 +7249,7 @@ } }, "traits": { - "smithy.api#documentation": "

Details for a volume mount point that's used in a container definition.

" + "smithy.api#documentation": "

The details for a volume mount point that's used in a container definition.

" } }, "com.amazonaws.ecs#MountPointList": { @@ -7591,7 +7644,7 @@ "hostPort": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

The port number on the container instance to reserve for your container.

\n

If you specify a containerPortRange, leave this field empty and the value of\n\t\t\tthe hostPort is set as follows:

\n
    \n
  • \n

    For containers in a task with the awsvpc network mode, the\n\t\t\t\t\t\thostPort is set to the same value as the\n\t\t\t\t\t\tcontainerPort. This is a static mapping strategy.

    \n
  • \n
  • \n

    For containers in a task with the bridge network mode, the Amazon ECS\n\t\t\t\t\tagent finds open ports on the host and automaticaly binds them to the container\n\t\t\t\t\tports. This is a dynamic mapping strategy.

    \n
  • \n
\n

If you use containers in a task with the awsvpc or host\n\t\t\tnetwork mode, the hostPort can either be left blank or set to the same\n\t\t\tvalue as the containerPort.

\n

If you use containers in a task with the bridge network mode, you can\n\t\t\tspecify a non-reserved host port for your container port mapping, or you can omit the\n\t\t\t\thostPort (or set it to 0) while specifying a\n\t\t\t\tcontainerPort and your container automatically receives a port in the\n\t\t\tephemeral port range for your container instance operating system and Docker\n\t\t\tversion.

\n

The default ephemeral port range for Docker version 1.6.0 and later is listed on the\n\t\t\tinstance under /proc/sys/net/ipv4/ip_local_port_range. If this kernel\n\t\t\tparameter is unavailable, the default ephemeral port range from 49153 through 65535 is\n\t\t\tused. Do not attempt to specify a host port in the ephemeral port range as these are\n\t\t\treserved for automatic assignment. In general, ports below 32768 are outside of the\n\t\t\tephemeral port range.

\n

The default reserved ports are 22 for SSH, the Docker ports 2375 and 2376, and the\n\t\t\tAmazon ECS container agent ports 51678-51680. Any host port that was previously specified in\n\t\t\ta running task is also reserved while the task is running. That is, after a task stops,\n\t\t\tthe host port is released. The current reserved ports are displayed in the\n\t\t\t\tremainingResources of DescribeContainerInstances\n\t\t\toutput. A container instance can have up to 100 reserved ports at a time. This number\n\t\t\tincludes the default reserved ports. Automatically assigned ports aren't included in the\n\t\t\t100 reserved ports quota.

" + "smithy.api#documentation": "

The port number on the container instance to reserve for your container.

\n

If you specify a containerPortRange, leave this field empty and the value of\n\t\t\tthe hostPort is set as follows:

\n
    \n
  • \n

    For containers in a task with the awsvpc network mode, the\n\t\t\t\t\t\thostPort is set to the same value as the\n\t\t\t\t\t\tcontainerPort. This is a static mapping strategy.

    \n
  • \n
  • \n

    For containers in a task with the bridge network mode, the Amazon ECS agent finds\n\t\t\t\t\topen ports on the host and automatically binds them to the container ports. This\n\t\t\t\t\tis a dynamic mapping strategy.

    \n
  • \n
\n

If you use containers in a task with the awsvpc or host\n\t\t\tnetwork mode, the hostPort can either be left blank or set to the same\n\t\t\tvalue as the containerPort.

\n

If you use containers in a task with the bridge network mode, you can\n\t\t\tspecify a non-reserved host port for your container port mapping, or you can omit the\n\t\t\t\thostPort (or set it to 0) while specifying a\n\t\t\t\tcontainerPort and your container automatically receives a port in the\n\t\t\tephemeral port range for your container instance operating system and Docker\n\t\t\tversion.

\n

The default ephemeral port range for Docker version 1.6.0 and later is listed on the\n\t\t\tinstance under /proc/sys/net/ipv4/ip_local_port_range. If this kernel\n\t\t\tparameter is unavailable, the default ephemeral port range from 49153 through 65535 is\n\t\t\tused. Do not attempt to specify a host port in the ephemeral port range as these are\n\t\t\treserved for automatic assignment. In general, ports below 32768 are outside of the\n\t\t\tephemeral port range.

\n

The default reserved ports are 22 for SSH, the Docker ports 2375 and 2376, and the\n\t\t\tAmazon ECS container agent ports 51678-51680. Any host port that was previously specified in\n\t\t\ta running task is also reserved while the task is running. That is, after a task stops,\n\t\t\tthe host port is released. The current reserved ports are displayed in the\n\t\t\tremainingResources of DescribeContainerInstances\n\t\t\toutput. A container instance can have up to 100 reserved ports at a time. This number\n\t\t\tincludes the default reserved ports. Automatically assigned ports aren't included in the\n\t\t\t100 reserved ports quota.

" } }, "protocol": { @@ -7620,7 +7673,7 @@ } }, "traits": { - "smithy.api#documentation": "

Port mappings allow containers to access ports on the host container instance to send\n\t\t\tor receive traffic. Port mappings are specified as part of the container\n\t\t\tdefinition.

\n

If you use containers in a task with the awsvpc or host\n\t\t\tnetwork mode, specify the exposed ports using containerPort. The\n\t\t\t\thostPort can be left blank or it must be the same value as the\n\t\t\t\tcontainerPort.

\n \n

You can't expose the same container port for multiple protocols. If you attempt\n\t\t\t\tthis, an error is returned.

\n
\n

After a task reaches the RUNNING status, manual and automatic host and\n\t\t\tcontainer port assignments are visible in the networkBindings section of\n\t\t\t\tDescribeTasks API responses.

" + "smithy.api#documentation": "

Port mappings allow containers to access ports on the host container instance to send\n\t\t\tor receive traffic. Port mappings are specified as part of the container\n\t\t\tdefinition.

\n

If you use containers in a task with the awsvpc or host\n\t\t\tnetwork mode, specify the exposed ports using containerPort. The\n\t\t\t\thostPort can be left blank or it must be the same value as the\n\t\t\t\tcontainerPort.

\n

Most fields of this parameter (containerPort, hostPort,\n\t\t\t\tprotocol) maps to PortBindings in the\n\t\t\tCreate a container section of the Docker Remote API and the\n\t\t\t\t--publish option to \n docker\n\t\t\t\t\trun\n . If the network mode of a task definition is set to\n\t\t\t\thost, host ports must either be undefined or match the container port\n\t\t\tin the port mapping.

\n \n

You can't expose the same container port for multiple protocols. If you attempt\n\t\t\t\tthis, an error is returned.

\n
\n

After a task reaches the RUNNING status, manual and automatic host and\n\t\t\tcontainer port assignments are visible in the networkBindings section of\n\t\t\t\tDescribeTasks API responses.

" } }, "com.amazonaws.ecs#PortMappingList": { @@ -7758,7 +7811,7 @@ } ], "traits": { - "smithy.api#documentation": "

Modifies an account setting. Account settings are set on a per-Region basis.

\n

If you change the account setting for the root user, the default settings for all of\n\t\t\tthe users and roles that no individual account setting was specified are reset for.\n\t\t\tFor more information, see Account\n\t\t\t\tSettings in the Amazon Elastic Container Service Developer Guide.

\n

When serviceLongArnFormat, taskLongArnFormat, or\n\t\t\t\tcontainerInstanceLongArnFormat are specified, the Amazon Resource Name\n\t\t\t(ARN) and resource ID format of the resource type for a specified user, role, or\n\t\t\tthe root user for an account is affected. The opt-in and opt-out account setting must be\n\t\t\tset for each Amazon ECS resource separately. The ARN and resource ID format of a resource\n\t\t\tis defined by the opt-in status of the user or role that created the resource. You\n\t\t\tmust turn on this setting to use Amazon ECS features such as resource tagging.

\n

When awsvpcTrunking is specified, the elastic network interface (ENI)\n\t\t\tlimit for any new container instances that support the feature is changed. If\n\t\t\t\tawsvpcTrunking is enabled, any new container instances that support the\n\t\t\tfeature are launched have the increased ENI limits available to them. For more\n\t\t\tinformation, see Elastic Network\n\t\t\t\tInterface Trunking in the Amazon Elastic Container Service Developer Guide.

\n

When containerInsights is specified, the default setting indicating\n\t\t\twhether CloudWatch Container Insights is enabled for your clusters is changed. If\n\t\t\t\tcontainerInsights is enabled, any new clusters that are created will\n\t\t\thave Container Insights enabled unless you disable it during cluster creation. For more\n\t\t\tinformation, see CloudWatch\n\t\t\t\tContainer Insights in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Modifies an account setting. Account settings are set on a per-Region basis.

\n

If you change the root user account setting, the default settings are reset for users\n\t\t\tand roles that do not have specified individual account settings. For more information,\n\t\t\tsee Account\n\t\t\t\tSettings in the Amazon Elastic Container Service Developer Guide.

\n

When serviceLongArnFormat, taskLongArnFormat, or\n\t\t\t\tcontainerInstanceLongArnFormat are specified, the Amazon Resource Name\n\t\t\t(ARN) and resource ID format of the resource type for a specified user, role, or\n\t\t\tthe root user for an account is affected. The opt-in and opt-out account setting must be\n\t\t\tset for each Amazon ECS resource separately. The ARN and resource ID format of a resource\n\t\t\tis defined by the opt-in status of the user or role that created the resource. You\n\t\t\tmust turn on this setting to use Amazon ECS features such as resource tagging.

\n

When awsvpcTrunking is specified, the elastic network interface (ENI)\n\t\t\tlimit for any new container instances that support the feature is changed. If\n\t\t\t\tawsvpcTrunking is turned on, any new container instances that support the\n\t\t\tfeature are launched have the increased ENI limits available to them. For more\n\t\t\tinformation, see Elastic Network\n\t\t\t\tInterface Trunking in the Amazon Elastic Container Service Developer Guide.

\n

When containerInsights is specified, the default setting indicating whether\n\t\t\tAmazon Web Services CloudWatch Container Insights is turned on for your clusters is changed. If\n\t\t\t\tcontainerInsights is turned on, any new clusters that are created will\n\t\t\thave Container Insights turned on unless you disable it during cluster creation. For\n\t\t\tmore information, see CloudWatch\n\t\t\t\tContainer Insights in the Amazon Elastic Container Service Developer Guide.

\n

Amazon ECS is introducing tagging authorization for resource creation. Users must have\n\t\t\tpermissions for actions that create the resource, such as ecsCreateCluster.\n\t\t\tIf tags are specified when you create a resource, Amazon Web Services performs additional\n\t\t\tauthorization to verify if users or roles have permissions to create tags. Therefore,\n\t\t\tyou must grant explicit permissions to use the ecs:TagResource action. For\n\t\t\tmore information, see Grant\n\t\t\t\tpermission to tag resources on creation in the Amazon ECS Developer\n\t\t\t\t\tGuide.

" } }, "com.amazonaws.ecs#PutAccountSettingDefault": { @@ -7790,14 +7843,14 @@ "name": { "target": "com.amazonaws.ecs#SettingName", "traits": { - "smithy.api#documentation": "

The resource name for which to modify the account setting. If\n\t\t\t\tserviceLongArnFormat is specified, the ARN for your Amazon ECS services is\n\t\t\taffected. If taskLongArnFormat is specified, the ARN and resource ID for\n\t\t\tyour Amazon ECS tasks is affected. If containerInstanceLongArnFormat is\n\t\t\tspecified, the ARN and resource ID for your Amazon ECS container instances is affected. If\n\t\t\t\tawsvpcTrunking is specified, the ENI limit for your Amazon ECS container\n\t\t\tinstances is affected. If containerInsights is specified, the default\n\t\t\tsetting for CloudWatch Container Insights for your clusters is affected.

\n

Fargate is transitioning from task count-based quotas to vCPU-based quotas. You can\n\t\t\tset the name to fargateVCPULimit to opt in or opt out of the vCPU-based\n\t\t\tquotas. For information about the opt in timeline, see Fargate vCPU-based quotas timeline in the\n\t\t\t\tAmazon ECS Developer Guide.

", + "smithy.api#documentation": "

The resource name for which to modify the account setting. If\n\t\t\t\tserviceLongArnFormat is specified, the ARN for your Amazon ECS services is\n\t\t\taffected. If taskLongArnFormat is specified, the ARN and resource ID for\n\t\t\tyour Amazon ECS tasks is affected. If containerInstanceLongArnFormat is\n\t\t\tspecified, the ARN and resource ID for your Amazon ECS container instances is affected. If\n\t\t\t\tawsvpcTrunking is specified, the ENI limit for your Amazon ECS container\n\t\t\tinstances is affected. If containerInsights is specified, the default\n\t\t\tsetting for Amazon Web Services CloudWatch Container Insights for your clusters is affected. If\n\t\t\t\ttagResourceAuthorization is specified, the opt-in option for tagging\n\t\t\tresources on creation is affected. For information about the opt-in timeline, see Tagging authorization timeline in the Amazon ECS Developer\n\t\t\t\tGuide.

\n

When you specify fargateFIPSMode for the name and\n\t\t\tenabled for the value, Fargate uses FIPS-140 compliant\n\t\t\tcryptographic algorithms on your tasks. For more information about FIPS-140 compliance\n\t\t\twith Fargate, see Amazon Web Services Fargate Federal Information Processing Standard (FIPS) 140-2\n\t\t\t\tcompliance in the Amazon Elastic Container Service Developer Guide.

", "smithy.api#required": {} } }, "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The account setting value for the specified principal ARN. Accepted values are\n\t\t\t\tenabled and disabled.

", + "smithy.api#documentation": "

The account setting value for the specified principal ARN. Accepted values are\n\t\t\t\tenabled, disabled, on, and\n\t\t\toff.

", "smithy.api#required": {} } } @@ -7826,14 +7879,14 @@ "name": { "target": "com.amazonaws.ecs#SettingName", "traits": { - "smithy.api#documentation": "

The Amazon ECS resource name for which to modify the account setting. If\n\t\t\t\tserviceLongArnFormat is specified, the ARN for your Amazon ECS services is\n\t\t\taffected. If taskLongArnFormat is specified, the ARN and resource ID for\n\t\t\tyour Amazon ECS tasks is affected. If containerInstanceLongArnFormat is\n\t\t\tspecified, the ARN and resource ID for your Amazon ECS container instances is affected. If\n\t\t\t\tawsvpcTrunking is specified, the elastic network interface (ENI) limit\n\t\t\tfor your Amazon ECS container instances is affected. If containerInsights is\n\t\t\tspecified, the default setting for CloudWatch Container Insights for your clusters is\n\t\t\taffected.

", + "smithy.api#documentation": "

The Amazon ECS resource name for which to modify the account setting. If\n\t\t\t\tserviceLongArnFormat is specified, the ARN for your Amazon ECS services is\n\t\t\taffected. If taskLongArnFormat is specified, the ARN and resource ID for\n\t\t\tyour Amazon ECS tasks is affected. If containerInstanceLongArnFormat is\n\t\t\tspecified, the ARN and resource ID for your Amazon ECS container instances is affected. If\n\t\t\t\tawsvpcTrunking is specified, the elastic network interface (ENI) limit\n\t\t\tfor your Amazon ECS container instances is affected. If containerInsights is\n\t\t\tspecified, the default setting for Amazon Web Services CloudWatch Container Insights for your clusters is\n\t\t\taffected. If fargateFIPSMode is specified, Fargate FIPS 140 compliance is\n\t\t\taffected. If tagResourceAuthorization is specified, the opt-in option for\n\t\t\ttagging resources on creation is affected. For information about the opt-in timeline,\n\t\t\tsee Tagging authorization timeline in the Amazon ECS Developer\n\t\t\t\t\tGuide.

", "smithy.api#required": {} } }, "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The account setting value for the specified principal ARN. Accepted values are\n\t\t\t\tenabled and disabled.

", + "smithy.api#documentation": "

The account setting value for the specified principal ARN. Accepted values are\n\t\t\t\tenabled, disabled, on, and\n\t\t\toff.

", "smithy.api#required": {} } }, @@ -8218,7 +8271,7 @@ "ephemeralStorage": { "target": "com.amazonaws.ecs#EphemeralStorage", "traits": { - "smithy.api#documentation": "

The amount of ephemeral storage to allocate for the task. This parameter is used to\n\t\t\texpand the total amount of ephemeral storage available, beyond the default amount, for\n\t\t\ttasks hosted on Fargate. For more information, see Fargate task\n\t\t\t\tstorage in the Amazon ECS User Guide for Fargate.

\n \n

This parameter is only supported for tasks hosted on Fargate using\n\t\t\t\tthe following platform versions:

\n
    \n
  • \n

    Linux platform version 1.4.0 or later.

    \n
  • \n
\n
" + "smithy.api#documentation": "

The amount of ephemeral storage to allocate for the task. This parameter is used to\n\t\t\texpand the total amount of ephemeral storage available, beyond the default amount, for\n\t\t\ttasks hosted on Fargate. For more information, see Fargate task\n\t\t\t\tstorage in the Amazon ECS User Guide for Fargate.

\n \n

For tasks using the Fargate launch type, the task requires\n\t\t\t\tthe following platforms:

\n
    \n
  • \n

    Linux platform version 1.4.0 or later.

    \n
  • \n
  • \n

    Windows platform version 1.0.0 or later.

    \n
  • \n
\n
" } }, "runtimePlatform": { @@ -8350,7 +8403,7 @@ "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The value for the specified resource type.

\n

If the GPU type is used, the value is the number of physical\n\t\t\t\tGPUs the Amazon ECS container agent reserves for the container. The number\n\t\t\tof GPUs that's reserved for all containers in a task can't exceed the number of\n\t\t\tavailable GPUs on the container instance that the task is launched on.

\n

If the InferenceAccelerator type is used, the value matches\n\t\t\tthe deviceName for an InferenceAccelerator specified in a\n\t\t\ttask definition.

", + "smithy.api#documentation": "

The value for the specified resource type.

\n

If the GPU type is used, the value is the number of physical\n\t\t\t\tGPUs the Amazon ECS container agent reserves for the container. The number\n\t\t\tof GPUs that's reserved for all containers in a task can't exceed the number of\n\t\t\tavailable GPUs on the container instance that the task is launched on.

\n

If the InferenceAccelerator type is used, the value matches\n\t\t\tthe deviceName for an InferenceAccelerator specified in a\n\t\t\ttask definition.

", "smithy.api#required": {} } }, @@ -8363,7 +8416,7 @@ } }, "traits": { - "smithy.api#documentation": "

The type and amount of a resource to assign to a container. The supported resource\n\t\t\ttypes are GPUs and Elastic Inference accelerators. For more information, see Working with\n\t\t\t\tGPUs on Amazon ECS or Working with\n\t\t\t\tAmazon Elastic Inference on Amazon ECS in the Amazon Elastic Container Service Developer Guide\n

" + "smithy.api#documentation": "

The type and amount of a resource to assign to a container. The supported resource types are\n\t\t\tGPUs and Elastic Inference accelerators. For more information, see Working with\n\t\t\t\tGPUs on Amazon ECS or Working with\n\t\t\t\tAmazon Elastic Inference on Amazon ECS in the Amazon Elastic Container Service Developer Guide\n

" } }, "com.amazonaws.ecs#ResourceRequirements": { @@ -8433,7 +8486,7 @@ } ], "traits": { - "smithy.api#documentation": "

Starts a new task using the specified task definition.

\n

You can allow Amazon ECS to place tasks for you, or you can customize how Amazon ECS places\n\t\t\ttasks using placement constraints and placement strategies. For more information, see\n\t\t\t\tScheduling Tasks in the Amazon Elastic Container Service Developer Guide.

\n

Alternatively, you can use StartTask to use your own scheduler or\n\t\t\tplace tasks manually on specific container instances.

\n

The Amazon ECS API follows an eventual consistency model. This is because of the\n\t\t\tdistributed nature of the system supporting the API. This means that the result of an\n\t\t\tAPI command you run that affects your Amazon ECS resources might not be immediately visible\n\t\t\tto all subsequent commands you run. Keep this in mind when you carry out an API command\n\t\t\tthat immediately follows a previous API command.

\n

To manage eventual consistency, you can do the following:

\n
    \n
  • \n

    Confirm the state of the resource before you run a command to modify it. Run\n\t\t\t\t\tthe DescribeTasks command using an exponential backoff algorithm to ensure that\n\t\t\t\t\tyou allow enough time for the previous command to propagate through the system.\n\t\t\t\t\tTo do this, run the DescribeTasks command repeatedly, starting with a couple of\n\t\t\t\t\tseconds of wait time and increasing gradually up to five minutes of wait\n\t\t\t\t\ttime.

    \n
  • \n
  • \n

    Add wait time between subsequent commands, even if the DescribeTasks command\n\t\t\t\t\treturns an accurate response. Apply an exponential backoff algorithm starting\n\t\t\t\t\twith a couple of seconds of wait time, and increase gradually up to about five\n\t\t\t\t\tminutes of wait time.

    \n
  • \n
" + "smithy.api#documentation": "

Starts a new task using the specified task definition.

\n

You can allow Amazon ECS to place tasks for you, or you can customize how Amazon ECS places\n\t\t\ttasks using placement constraints and placement strategies. For more information, see\n\t\t\t\tScheduling Tasks in the Amazon Elastic Container Service Developer Guide.

\n

Alternatively, you can use StartTask to use your own scheduler or\n\t\t\tplace tasks manually on specific container instances.

\n \n

Starting April 15, 2023, Amazon Web Services will not onboard new customers to Amazon Elastic Inference (EI), and will help current customers migrate their workloads to options that offer better price and performance. After April 15, 2023, new customers will not be able to launch instances with Amazon EI accelerators in Amazon SageMaker, Amazon ECS, or Amazon EC2. However, customers who have used Amazon EI at least once during the past 30-day period are considered current customers and will be able to continue using the service.

\n
\n

The Amazon ECS API follows an eventual consistency model. This is because of the\n\t\t\tdistributed nature of the system supporting the API. This means that the result of an\n\t\t\tAPI command you run that affects your Amazon ECS resources might not be immediately visible\n\t\t\tto all subsequent commands you run. Keep this in mind when you carry out an API command\n\t\t\tthat immediately follows a previous API command.

\n

To manage eventual consistency, you can do the following:

\n
    \n
  • \n

    Confirm the state of the resource before you run a command to modify it. Run\n\t\t\t\t\tthe DescribeTasks command using an exponential backoff algorithm to ensure that\n\t\t\t\t\tyou allow enough time for the previous command to propagate through the system.\n\t\t\t\t\tTo do this, run the DescribeTasks command repeatedly, starting with a couple of\n\t\t\t\t\tseconds of wait time and increasing gradually up to five minutes of wait\n\t\t\t\t\ttime.

    \n
  • \n
  • \n

    Add wait time between subsequent commands, even if the DescribeTasks command\n\t\t\t\t\treturns an accurate response. Apply an exponential backoff algorithm starting\n\t\t\t\t\twith a couple of seconds of wait time, and increase gradually up to about five\n\t\t\t\t\tminutes of wait time.

    \n
  • \n
" } }, "com.amazonaws.ecs#RunTaskRequest": { @@ -8892,12 +8945,12 @@ "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Determines whether the execute command functionality is enabled for the service. If\n\t\t\t\ttrue, the execute command functionality is enabled for all containers\n\t\t\tin tasks as part of the service.

" + "smithy.api#documentation": "

Determines whether the execute command functionality is turned on for the service. If\n\t\t\t\ttrue, the execute command functionality is turned on for all containers\n\t\t\tin tasks as part of the service.

" } } }, "traits": { - "smithy.api#documentation": "

Details on a service within a cluster

" + "smithy.api#documentation": "

Details on a service within a cluster.

" } }, "com.amazonaws.ecs#ServiceConnectClientAlias": { @@ -8941,7 +8994,7 @@ "namespace": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The namespace name or full Amazon Resource Name (ARN) of the Cloud Map namespace for use with Service Connect. The namespace must be in\n\t\t\tthe same Amazon Web Services Region as the Amazon ECS service and cluster. The type of namespace doesn't\n\t\t\taffect Service Connect. For more information about Cloud Map, see Working with Services in the\n\t\t\tCloud Map Developer Guide.

" + "smithy.api#documentation": "

The namespace name or full Amazon Resource Name (ARN) of the Cloud Map namespace for use with Service Connect. The namespace must be in\n\t\t\tthe same Amazon Web Services Region as the Amazon ECS service and cluster. The type of namespace doesn't\n\t\t\taffect Service Connect. For more information about Cloud Map, see Working with Services in the\n\t\t\tCloud Map Developer Guide.

" } }, "services": { @@ -8971,7 +9024,7 @@ "discoveryName": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The discoveryName is the name of the new Cloud Map service that Amazon ECS creates\n\t\t\tfor this Amazon ECS service. This must be unique within the Cloud Map namespace. The name can contain up to 64 characters. The name can include lowercase letters,\n\t\t\tnumbers, underscores (_), and hyphens (-). The name can't start with a hyphen.

\n

If this parameter isn't specified, the default value of discoveryName.namespace is used. If the discoveryName isn't specified, the port mapping name from the task definition is used in portName.namespace.

" + "smithy.api#documentation": "

The discoveryName is the name of the new Cloud Map service that Amazon ECS creates\n\t\t\tfor this Amazon ECS service. This must be unique within the Cloud Map namespace. The name can contain up to 64 characters. The name can include lowercase letters,\n\t\t\tnumbers, underscores (_), and hyphens (-). The name can't start with a hyphen.

\n

If the discoveryName isn't specified, the port mapping name from the task definition is used in portName.namespace.

" } }, "clientAliases": { @@ -9003,7 +9056,7 @@ "discoveryName": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The discovery name of this Service Connect resource.

\n

The discoveryName is the name of the new Cloud Map service that Amazon ECS creates\n\t\t\tfor this Amazon ECS service. This must be unique within the Cloud Map namespace. The name can contain up to 64 characters. The name can include lowercase letters,\n\t\t\tnumbers, underscores (_), and hyphens (-). The name can't start with a hyphen.

\n

If this parameter isn't specified, the default value of discoveryName.namespace is used. If the discoveryName isn't specified, the port mapping name from the task definition is used in portName.namespace.

" + "smithy.api#documentation": "

The discovery name of this Service Connect resource.

\n

The discoveryName is the name of the new Cloud Map service that Amazon ECS creates\n\t\t\tfor this Amazon ECS service. This must be unique within the Cloud Map namespace. The name can contain up to 64 characters. The name can include lowercase letters,\n\t\t\tnumbers, underscores (_), and hyphens (-). The name can't start with a hyphen.

\n

If the discoveryName isn't specified, the port mapping name from the task definition is used in portName.namespace.

" } }, "discoveryArn": { @@ -9224,6 +9277,18 @@ "traits": { "smithy.api#enumValue": "containerInsights" } + }, + "FARGATE_FIPS_MODE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "fargateFIPSMode" + } + }, + "TAG_RESOURCE_AUTHORIZATION": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "tagResourceAuthorization" + } } } }, @@ -9290,7 +9355,7 @@ } ], "traits": { - "smithy.api#documentation": "

Starts a new task from the specified task definition on the specified container\n\t\t\tinstance or instances.

\n

Alternatively, you can use RunTask to place tasks for you. For more\n\t\t\tinformation, see Scheduling Tasks in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Starts a new task from the specified task definition on the specified container\n\t\t\tinstance or instances.

\n \n

Starting April 15, 2023, Amazon Web Services will not onboard new customers to Amazon Elastic Inference (EI), and will help current customers migrate their workloads to options that offer better price and performance. After April 15, 2023, new customers will not be able to launch instances with Amazon EI accelerators in Amazon SageMaker, Amazon ECS, or Amazon EC2. However, customers who have used Amazon EI at least once during the past 30-day period are considered current customers and will be able to continue using the service.

\n
\n

Alternatively, you can use RunTask to place tasks for you. For more\n\t\t\tinformation, see Scheduling Tasks in the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#StartTaskRequest": { @@ -9320,7 +9385,7 @@ "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Whether or not the execute command functionality is enabled for the task. If\n\t\t\t\ttrue, this enables execute command functionality on all containers in\n\t\t\tthe task.

" + "smithy.api#documentation": "

Whether or not the execute command functionality is turned on for the task. If\n\t\t\t\ttrue, this turns on the execute command functionality on all containers in\n\t\t\tthe task.

" } }, "group": { @@ -9900,7 +9965,7 @@ } }, "traits": { - "smithy.api#documentation": "

The execute command cannot run. This error can be caused by any of the following\n\t\t\tconfiguration issues:

\n
    \n
  • \n

    Incorrect IAM permissions

    \n
  • \n
  • \n

    The SSM agent is not installed or is not running

    \n
  • \n
  • \n

    There is an interface Amazon VPC endpoint for Amazon ECS, but there is not one for\n\t\t\t\t\tfor Systems Manager Session Manager

    \n
  • \n
\n

For information about how to troubleshoot the issues, see Troubleshooting issues with ECS\n\t\t\t\tExec in the Amazon Elastic Container Service Developer Guide.

", + "smithy.api#documentation": "

The execute command cannot run. This error can be caused by any of the following\n\t\t\tconfiguration issues:

\n
    \n
  • \n

    Incorrect IAM permissions

    \n
  • \n
  • \n

    The SSM agent is not installed or is not running

    \n
  • \n
  • \n

    There is an interface Amazon VPC endpoint for Amazon ECS, but there is not one for Systems\n\t\t\t\t\tManager Session Manager

    \n
  • \n
\n

For information about how to troubleshoot the issues, see Troubleshooting issues with ECS\n\t\t\t\tExec in the Amazon Elastic Container Service Developer Guide.

", "smithy.api#error": "client" } }, @@ -10006,7 +10071,7 @@ "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Determines whether execute command functionality is enabled for this task. If\n\t\t\t\ttrue, execute command functionality is enabled on all the containers in\n\t\t\tthe task.

" + "smithy.api#documentation": "

Determines whether execute command functionality is turned on for this task. If\n\t\t\t\ttrue, execute command functionality is turned on all the containers in\n\t\t\tthe task.

" } }, "executionStoppedAt": { @@ -10238,7 +10303,7 @@ "requiresCompatibilities": { "target": "com.amazonaws.ecs#CompatibilityList", "traits": { - "smithy.api#documentation": "

The task launch types the task definition was validated against. To determine which\n\t\t\ttask launch types the task definition is validated for, see the TaskDefinition$compatibilities parameter.

" + "smithy.api#documentation": "

The task launch types the task definition was validated against. For more information, see Amazon ECS launch types\n\t\t\tin the Amazon Elastic Container Service Developer Guide.

" } }, "cpu": { @@ -10369,7 +10434,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object representing a constraint on task placement in the task definition. For more\n\t\t\tinformation, see Task placement constraints in the\n\t\t\tAmazon Elastic Container Service Developer Guide.

\n \n

Task placement constraints aren't supported for tasks run on Fargate.

\n
" + "smithy.api#documentation": "

The constraint on task placement in the task definition. For more\n\t\t\tinformation, see Task placement constraints in the\n\t\t\tAmazon Elastic Container Service Developer Guide.

\n \n

Task placement constraints aren't supported for tasks run on Fargate.

\n
" } }, "com.amazonaws.ecs#TaskDefinitionPlacementConstraintType": { @@ -11050,6 +11115,9 @@ { "target": "com.amazonaws.ecs#InvalidParameterException" }, + { + "target": "com.amazonaws.ecs#NamespaceNotFoundException" + }, { "target": "com.amazonaws.ecs#ServerException" } @@ -11615,7 +11683,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates the protection status of a task. You can set protectionEnabled to\n\t\t\t\ttrue to protect your task from termination during scale-in events from\n\t\t\t\tService\n\t\t\t\tAutoscaling or deployments.

\n

Task-protection, by default, expires after 2 hours at which point Amazon ECS unsets the\n\t\t\t\tprotectionEnabled property making the task eligible for termination by\n\t\t\ta subsequent scale-in event.

\n

You can specify a custom expiration period for task protection from 1 minute to up to\n\t\t\t2,880 minutes (48 hours). To specify the custom expiration period, set the\n\t\t\t\texpiresInMinutes property. The expiresInMinutes property\n\t\t\tis always reset when you invoke this operation for a task that already has\n\t\t\t\tprotectionEnabled set to true. You can keep extending the\n\t\t\tprotection expiration period of a task by invoking this operation repeatedly.

\n

To learn more about Amazon ECS task protection, see Task scale-in\n\t\t\t\tprotection in the \n Amazon Elastic Container Service Developer Guide\n .

\n \n

This operation is only supported for tasks belonging to an Amazon ECS service. Invoking\n\t\t\t\tthis operation for a standalone task will result in an TASK_NOT_VALID\n\t\t\t\tfailure. For more information, see API failure\n\t\t\t\t\treasons.

\n
\n \n

If you prefer to set task protection from within the container, we recommend using\n\t\t\t\tthe Task scale-in protection endpoint.

\n
" + "smithy.api#documentation": "

Updates the protection status of a task. You can set protectionEnabled to\n\t\t\t\ttrue to protect your task from termination during scale-in events from\n\t\t\t\tService\n\t\t\t\tAutoscaling or deployments.

\n

Task-protection, by default, expires after 2 hours at which point Amazon ECS clears the\n\t\t\t\tprotectionEnabled property making the task eligible for termination by\n\t\t\ta subsequent scale-in event.

\n

You can specify a custom expiration period for task protection from 1 minute to up to\n\t\t\t2,880 minutes (48 hours). To specify the custom expiration period, set the\n\t\t\t\texpiresInMinutes property. The expiresInMinutes property\n\t\t\tis always reset when you invoke this operation for a task that already has\n\t\t\t\tprotectionEnabled set to true. You can keep extending the\n\t\t\tprotection expiration period of a task by invoking this operation repeatedly.

\n

To learn more about Amazon ECS task protection, see Task scale-in\n\t\t\t\tprotection in the \n Amazon Elastic Container Service Developer Guide\n .

\n \n

This operation is only supported for tasks belonging to an Amazon ECS service. Invoking\n\t\t\t\tthis operation for a standalone task will result in an TASK_NOT_VALID\n\t\t\t\tfailure. For more information, see API failure\n\t\t\t\t\treasons.

\n
\n \n

If you prefer to set task protection from within the container, we recommend using\n\t\t\t\tthe Task scale-in protection endpoint.

\n
" } }, "com.amazonaws.ecs#UpdateTaskProtectionRequest": { @@ -11660,7 +11728,7 @@ "protectedTasks": { "target": "com.amazonaws.ecs#ProtectedTasks", "traits": { - "smithy.api#documentation": "

A list of tasks with the following information.

\n
    \n
  • \n

    \n taskArn: The task ARN.

    \n
  • \n
  • \n

    \n protectionEnabled: The protection status of the task. If scale-in\n\t\t\t\t\tprotection is enabled for a task, the value is true. Otherwise, it\n\t\t\t\t\tis false.

    \n
  • \n
  • \n

    \n expirationDate: The epoch time when protection for the task will\n\t\t\t\t\texpire.

    \n
  • \n
" + "smithy.api#documentation": "

A list of tasks with the following information.

\n
    \n
  • \n

    \n taskArn: The task ARN.

    \n
  • \n
  • \n

    \n protectionEnabled: The protection status of the task. If scale-in\n\t\t\t\t\tprotection is turned on for a task, the value is true. Otherwise, it\n\t\t\t\t\tis false.

    \n
  • \n
  • \n

    \n expirationDate: The epoch time when protection for the task will\n\t\t\t\t\texpire.

    \n
  • \n
" } }, "failures": { diff --git a/aws/sdk/aws-models/glacier.json b/aws/sdk/aws-models/glacier.json index 6fc9d21fe1..32cb84212f 100644 --- a/aws/sdk/aws-models/glacier.json +++ b/aws/sdk/aws-models/glacier.json @@ -2065,44 +2065,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-east-1" - ] - } - ], - "endpoint": { - "url": "https://glacier.us-gov-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-west-1" - ] - } - ], - "endpoint": { - "url": "https://glacier.us-gov-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -2138,8 +2100,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2151,8 +2113,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2164,8 +2126,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2177,8 +2139,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2190,8 +2152,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2203,8 +2165,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2216,8 +2178,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2229,8 +2191,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2242,8 +2204,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2255,8 +2217,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2268,8 +2230,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2281,8 +2243,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2294,8 +2256,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2307,8 +2269,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2320,8 +2282,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2333,8 +2295,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2346,8 +2308,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2359,8 +2321,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2372,8 +2334,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2385,8 +2347,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2398,8 +2360,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2411,8 +2373,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2424,8 +2386,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2437,8 +2399,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2450,8 +2412,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2463,8 +2425,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2476,8 +2438,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2489,8 +2451,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2502,8 +2464,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2515,8 +2477,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2528,8 +2490,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2541,8 +2503,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2554,8 +2516,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2567,8 +2529,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2580,8 +2542,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2593,8 +2555,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2606,8 +2568,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2619,8 +2581,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2632,8 +2594,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2645,8 +2607,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2658,8 +2620,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2671,8 +2633,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2684,8 +2657,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2697,8 +2681,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2710,8 +2705,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2723,8 +2729,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2736,8 +2742,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2748,8 +2754,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2760,10 +2766,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/iam.json b/aws/sdk/aws-models/iam.json index bd2c1ac793..3f222206c6 100644 --- a/aws/sdk/aws-models/iam.json +++ b/aws/sdk/aws-models/iam.json @@ -665,216 +665,91 @@ }, "aws" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] + "ref": "UseFIPS" }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://iam.amazonaws.com", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-east-1" } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "aws" ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } + true ] }, { - "conditions": [], - "endpoint": { - "url": "https://iam.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://iam-fips.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -892,208 +767,40 @@ }, "aws-cn" ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" - } - ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.amazonaws.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://iam.cn-north-1.amazonaws.com.cn", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" + "name": "sigv4", + "signingName": "iam", + "signingRegion": "cn-north-1" } ] }, - { - "conditions": [], - "endpoint": { - "url": "https://iam.cn-north-1.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "cn-north-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1111,216 +818,91 @@ }, "aws-us-gov" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] + "ref": "UseFIPS" }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://iam.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-gov-west-1" } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "aws-us-gov" ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } + true ] }, { - "conditions": [], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-gov-west-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://iam.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-gov-west-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1338,80 +920,40 @@ }, "aws-iso" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.c2s.ic.gov", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [], - "endpoint": { - "url": "https://iam.us-iso-east-1.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-iso-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://iam.us-iso-east-1.c2s.ic.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-iso-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1419,90 +961,50 @@ "fn": "stringEquals", "argv": [ { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "name" - ] - }, - "aws-iso-b" - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.sc2s.sgov.gov", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, + "aws-iso-b" + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "ref": "UseFIPS" + }, + false ] }, { - "conditions": [], - "endpoint": { - "url": "https://iam.us-isob-east-1.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-isob-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://iam.us-isob-east-1.sc2s.sgov.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-isob-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1624,60 +1126,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://iam-fips.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-us-gov-global" - ] - } - ], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1760,141 +1208,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://iam.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-cn-global" - ] - } - ], - "endpoint": { - "url": "https://iam.cn-north-1.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "cn-north-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-us-gov-global" - ] - } - ], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-global" - ] - } - ], - "endpoint": { - "url": "https://iam.us-iso-east-1.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-iso-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-b-global" - ] - } - ], - "endpoint": { - "url": "https://iam.us-isob-east-1.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-isob-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1938,9 +1251,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-global", "UseFIPS": false, - "Region": "aws-global" + "UseDualStack": false } }, { @@ -1960,9 +1273,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-global", "UseFIPS": true, - "Region": "aws-global" + "UseDualStack": false } }, { @@ -1973,9 +1286,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -1995,9 +1308,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -2008,9 +1321,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -2030,9 +1343,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -2052,9 +1365,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-cn-global", "UseFIPS": false, - "Region": "aws-cn-global" + "UseDualStack": false } }, { @@ -2065,9 +1378,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -2078,9 +1391,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -2091,9 +1404,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -2113,9 +1426,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -2135,9 +1448,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-us-gov-global", "UseFIPS": false, - "Region": "aws-us-gov-global" + "UseDualStack": false } }, { @@ -2157,9 +1470,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-us-gov-global", "UseFIPS": true, - "Region": "aws-us-gov-global" + "UseDualStack": false } }, { @@ -2170,9 +1483,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -2192,9 +1505,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -2205,9 +1518,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -2227,9 +1540,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -2249,9 +1562,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-iso-global", "UseFIPS": false, - "Region": "aws-iso-global" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2262,9 +1586,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2284,9 +1619,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -2306,9 +1641,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-iso-b-global", "UseFIPS": false, - "Region": "aws-iso-b-global" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2319,9 +1665,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2341,9 +1698,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false } }, { @@ -2354,9 +1711,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2368,8 +1725,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2379,9 +1736,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2391,11 +1748,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -3213,6 +2576,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#EntityAlreadyExistsException" }, @@ -3224,7 +2590,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates an alias for your Amazon Web Services account. For information about using an Amazon Web Services account\n alias, see Using an\n alias for your Amazon Web Services account ID in the\n IAM User Guide.

" + "smithy.api#documentation": "

Creates an alias for your Amazon Web Services account. For information about using an Amazon Web Services account\n alias, see Creating, deleting, and\n listing an Amazon Web Services account alias in the Amazon Web Services Sign-In User\n Guide.

" } }, "com.amazonaws.iam#CreateAccountAliasRequest": { @@ -4120,7 +3486,7 @@ "VirtualMFADeviceName": { "target": "com.amazonaws.iam#virtualMFADeviceName", "traits": { - "smithy.api#documentation": "

The name of the virtual MFA device, which must be unique. Use with path to uniquely identify a virtual MFA\n device.

\n

This parameter allows (through its regex pattern) a string of characters consisting of upper and lowercase alphanumeric \n characters with no spaces. You can also include any of the following characters: _+=,.@-

", + "smithy.api#documentation": "

The name of the virtual MFA device, which must be unique. Use with path to uniquely\n identify a virtual MFA device.

\n

This parameter allows (through its regex pattern) a string of characters consisting of upper and lowercase alphanumeric \n characters with no spaces. You can also include any of the following characters: _+=,.@-

", "smithy.api#required": {} } }, @@ -4211,6 +3577,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#EntityTemporarilyUnmodifiableException" }, @@ -4303,6 +3672,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#LimitExceededException" }, @@ -4314,7 +3686,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified Amazon Web Services account alias. For information about using an Amazon Web Services\n account alias, see Using an alias for your Amazon Web Services account ID in the\n IAM User Guide.

" + "smithy.api#documentation": "

Deletes the specified Amazon Web Services account alias. For information about using an Amazon Web Services\n account alias, see Creating, deleting, and\n listing an Amazon Web Services account alias in the Amazon Web Services Sign-In User\n Guide.

" } }, "com.amazonaws.iam#DeleteAccountAliasRequest": { @@ -4725,7 +4097,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the permissions boundary for the specified IAM role.

\n \n

Deleting the permissions boundary for a role might increase its permissions. For\n example, it might allow anyone who assumes the role to perform all the actions\n granted in its permissions policies.

\n
" + "smithy.api#documentation": "

Deletes the permissions boundary for the specified IAM role.

\n

You cannot set the boundary for a service-linked role.

\n \n

Deleting the permissions boundary for a role might increase its permissions. For\n example, it might allow anyone who assumes the role to perform all the actions\n granted in its permissions policies.

\n
" } }, "com.amazonaws.iam#DeleteRolePermissionsBoundaryRequest": { @@ -5027,6 +4399,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#LimitExceededException" }, @@ -5195,6 +4570,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#DeleteConflictException" }, @@ -5475,6 +4853,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#EntityAlreadyExistsException" }, @@ -8001,7 +7382,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the account alias associated with the Amazon Web Services account (Note: you can have only\n one). For information about using an Amazon Web Services account alias, see Using an alias for your\n Amazon Web Services account ID in the IAM User Guide.

", + "smithy.api#documentation": "

Lists the account alias associated with the Amazon Web Services account (Note: you can have only\n one). For information about using an Amazon Web Services account alias, see Creating,\n deleting, and listing an Amazon Web Services account alias in the Amazon Web Services Sign-In\n User Guide.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9720,7 +9101,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the IAM roles that have the specified path prefix. If there are none, the\n operation returns an empty list. For more information about roles, see Working with\n roles.

\n \n

IAM resource-listing operations return a subset of the available \n attributes for the resource. For example, this operation does not return tags, even though they are an attribute of the returned object. To view all of the information for a role, see GetRole.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", + "smithy.api#documentation": "

Lists the IAM roles that have the specified path prefix. If there are none, the\n operation returns an empty list. For more information about roles, see Working with\n roles.

\n \n

IAM resource-listing operations return a subset of the available \n attributes for the resource. This operation does not return the following attributes, even though they are an attribute of the returned object:

\n
    \n
  • \n

    PermissionsBoundary

    \n
  • \n
  • \n

    RoleLastUsed

    \n
  • \n
  • \n

    Tags

    \n
  • \n
\n

To view all of the information for a role, see GetRole.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -10443,7 +9824,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the IAM users that have the specified path prefix. If no path prefix is\n specified, the operation returns all users in the Amazon Web Services account. If there are none, the\n operation returns an empty list.

\n \n

IAM resource-listing operations return a subset of the available \n attributes for the resource. For example, this operation does not return tags, even though they are an attribute of the returned object. To view all of the information for a user, see GetUser.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", + "smithy.api#documentation": "

Lists the IAM users that have the specified path prefix. If no path prefix is\n specified, the operation returns all users in the Amazon Web Services account. If there are none, the\n operation returns an empty list.

\n \n

IAM resource-listing operations return a subset of the available \n attributes for the resource. This operation does not return the following attributes, even though they are an attribute of the returned object:

\n
    \n
  • \n

    PermissionsBoundary

    \n
  • \n
  • \n

    Tags

    \n
  • \n
\n

To view all of the information for a user, see GetUser.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -11411,7 +10792,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates the policy that is specified as the IAM role's permissions boundary.\n You can use an Amazon Web Services managed policy or a customer managed policy to set the boundary for\n a role. Use the boundary to control the maximum permissions that the role can have.\n Setting a permissions boundary is an advanced feature that can affect the permissions\n for the role.

\n

You cannot set the boundary for a service-linked role.

\n \n

Policies used as permissions boundaries do not provide permissions. You must also\n attach a permissions policy to the role. To learn how the effective permissions for\n a role are evaluated, see IAM JSON policy\n evaluation logic in the IAM User Guide.

\n
" + "smithy.api#documentation": "

Adds or updates the policy that is specified as the IAM role's permissions boundary.\n You can use an Amazon Web Services managed policy or a customer managed policy to set the boundary for\n a role. Use the boundary to control the maximum permissions that the role can have.\n Setting a permissions boundary is an advanced feature that can affect the permissions\n for the role.

\n

You cannot set the boundary for a service-linked role.

\n \n

Policies used as permissions boundaries do not provide permissions. You must also\n attach a permissions policy to the role. To learn how the effective permissions for\n a role are evaluated, see IAM JSON policy\n evaluation logic in the IAM User Guide.

\n
" } }, "com.amazonaws.iam#PutRolePermissionsBoundaryRequest": { @@ -11947,6 +11328,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#InvalidAuthenticationCodeException" }, @@ -13514,7 +12898,7 @@ "code": "UnmodifiableEntity", "httpResponseCode": 400 }, - "smithy.api#documentation": "

The request was rejected because only the service that depends on the service-linked role\n can modify or delete the role on your behalf. The error message includes the name of the\n service that depends on this service-linked role. You must request the change through that\n service.

", + "smithy.api#documentation": "

The request was rejected because service-linked roles are protected Amazon Web Services resources. Only\n the service that depends on the service-linked role can modify or delete the role on your\n behalf. The error message includes the name of the service that depends on this service-linked\n role. You must request the change through that service.

", "smithy.api#error": "client", "smithy.api#httpError": 400 } @@ -14874,6 +14258,9 @@ "target": "com.amazonaws.iam#UploadSigningCertificateResponse" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#DuplicateCertificateException" }, @@ -15076,7 +14463,7 @@ "Base32StringSeed": { "target": "com.amazonaws.iam#BootstrapDatum", "traits": { - "smithy.api#documentation": "

The base32 seed defined as specified in RFC3548. The Base32StringSeed is base64-encoded.

" + "smithy.api#documentation": "

The base32 seed defined as specified in RFC3548. The Base32StringSeed is base32-encoded.

" } }, "QRCodePNG": { @@ -15146,7 +14533,7 @@ "min": 3, "max": 63 }, - "smithy.api#pattern": "^[a-z0-9](([a-z0-9]|-(?!-))*[a-z0-9])?$" + "smithy.api#pattern": "^[a-z0-9]([a-z0-9]|-(?!-)){1,61}[a-z0-9]$" } }, "com.amazonaws.iam#arnType": { @@ -15600,7 +14987,7 @@ "min": 1, "max": 512 }, - "smithy.api#pattern": "^(\\u002F)|(\\u002F[\\u0021-\\u007F]+\\u002F)$" + "smithy.api#pattern": "^(\\u002F)|(\\u002F[\\u0021-\\u007E]+\\u002F)$" } }, "com.amazonaws.iam#policyDescriptionType": { @@ -15804,7 +15191,7 @@ "min": 0, "max": 1000 }, - "smithy.api#pattern": "^[\\p{L}\\p{M}\\p{Z}\\p{S}\\p{N}\\p{P}]*$" + "smithy.api#pattern": "^[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u00A1-\\u00FF]*$" } }, "com.amazonaws.iam#roleDetailListType": { diff --git a/aws/sdk/aws-models/kms.json b/aws/sdk/aws-models/kms.json index cd4fd4edd6..e161d67853 100644 --- a/aws/sdk/aws-models/kms.json +++ b/aws/sdk/aws-models/kms.json @@ -52,6 +52,18 @@ "traits": { "smithy.api#enumValue": "RSAES_OAEP_SHA_256" } + }, + "RSA_AES_KEY_WRAP_SHA_1": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSA_AES_KEY_WRAP_SHA_1" + } + }, + "RSA_AES_KEY_WRAP_SHA_256": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSA_AES_KEY_WRAP_SHA_256" + } } } }, @@ -135,6 +147,15 @@ } } }, + "com.amazonaws.kms#AttestationDocumentType": { + "type": "blob", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 262144 + } + } + }, "com.amazonaws.kms#BooleanType": { "type": "boolean", "traits": { @@ -214,7 +235,8 @@ "smithy.api#length": { "min": 19, "max": 24 - } + }, + "smithy.api#pattern": "^cluster-[2-7a-zA-Z]{11,16}$" } }, "com.amazonaws.kms#CloudHsmClusterInUseException": { @@ -542,7 +564,7 @@ "AliasName": { "target": "com.amazonaws.kms#AliasNameType", "traits": { - "smithy.api#documentation": "

Specifies the alias name. This value must begin with alias/ followed by a\n name, such as alias/ExampleAlias.

\n

The AliasName value must be string of 1-256 characters. It can contain only\n alphanumeric characters, forward slashes (/), underscores (_), and dashes (-). The alias name\n cannot begin with alias/aws/. The alias/aws/ prefix is reserved for\n Amazon Web Services managed\n keys.

", + "smithy.api#documentation": "

Specifies the alias name. This value must begin with alias/ followed by a\n name, such as alias/ExampleAlias.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

The AliasName value must be string of 1-256 characters. It can contain only\n alphanumeric characters, forward slashes (/), underscores (_), and dashes (-). The alias name\n cannot begin with alias/aws/. The alias/aws/ prefix is reserved for\n Amazon Web Services managed\n keys.

", "smithy.api#required": {} } }, @@ -629,7 +651,7 @@ "CustomKeyStoreName": { "target": "com.amazonaws.kms#CustomKeyStoreNameType", "traits": { - "smithy.api#documentation": "

Specifies a friendly name for the custom key store. The name must be unique in your\n Amazon Web Services account and Region. This parameter is required for all custom key stores.

", + "smithy.api#documentation": "

Specifies a friendly name for the custom key store. The name must be unique in your\n Amazon Web Services account and Region. This parameter is required for all custom key stores.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
", "smithy.api#required": {} } }, @@ -777,7 +799,7 @@ "Constraints": { "target": "com.amazonaws.kms#GrantConstraints", "traits": { - "smithy.api#documentation": "

Specifies a grant constraint.

\n

KMS supports the EncryptionContextEquals and\n EncryptionContextSubset grant constraints. Each constraint value can include up\n to 8 encryption context pairs. The encryption context value in each constraint cannot exceed\n 384 characters. For information about grant constraints, see Using grant\n constraints in the Key Management Service Developer Guide. For more information about encryption context,\n see Encryption\n context in the \n Key Management Service Developer Guide\n .

\n

The encryption context grant constraints allow the permissions in the grant only when the\n encryption context in the request matches (EncryptionContextEquals) or includes\n (EncryptionContextSubset) the encryption context specified in this structure.

\n

The encryption context grant constraints are supported only on grant operations that include\n an EncryptionContext parameter, such as cryptographic operations on symmetric\n encryption KMS keys. Grants with grant constraints can include the DescribeKey and RetireGrant operations, but the constraint doesn't apply to these\n operations. If a grant with a grant constraint includes the CreateGrant\n operation, the constraint requires that any grants created with the CreateGrant\n permission have an equally strict or stricter encryption context constraint.

\n

You cannot use an encryption context grant constraint for cryptographic operations with\n asymmetric KMS keys or HMAC KMS keys. These keys don't support an encryption context.

\n

" + "smithy.api#documentation": "

Specifies a grant constraint.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

KMS supports the EncryptionContextEquals and\n EncryptionContextSubset grant constraints, which allow the permissions in the\n grant only when the encryption context in the request matches\n (EncryptionContextEquals) or includes (EncryptionContextSubset)\n the encryption context specified in the constraint.

\n

The encryption context grant constraints are supported only on grant operations that include\n an EncryptionContext parameter, such as cryptographic operations on symmetric\n encryption KMS keys. Grants with grant constraints can include the DescribeKey and RetireGrant operations, but the constraint doesn't apply to these\n operations. If a grant with a grant constraint includes the CreateGrant\n operation, the constraint requires that any grants created with the CreateGrant\n permission have an equally strict or stricter encryption context constraint.

\n

You cannot use an encryption context grant constraint for cryptographic operations with\n asymmetric KMS keys or HMAC KMS keys. Operations with these keys don't support an encryption\n context.

\n

Each constraint value can include up to 8 encryption context pairs. The encryption context\n value in each constraint cannot exceed 384 characters. For information about grant\n constraints, see Using grant\n constraints in the Key Management Service Developer Guide. For more information about encryption context,\n see Encryption\n context in the \n Key Management Service Developer Guide\n .

" } }, "GrantTokens": { @@ -789,7 +811,7 @@ "Name": { "target": "com.amazonaws.kms#GrantNameType", "traits": { - "smithy.api#documentation": "

A friendly name for the grant. Use this value to prevent the unintended creation of\n duplicate grants when retrying this request.

\n

When this value is absent, all CreateGrant requests result in a new grant\n with a unique GrantId even if all the supplied parameters are identical. This can\n result in unintended duplicates when you retry the CreateGrant request.

\n

When this value is present, you can retry a CreateGrant request with\n identical parameters; if the grant already exists, the original GrantId is\n returned without creating a new grant. Note that the returned grant token is unique with every\n CreateGrant request, even when a duplicate GrantId is returned.\n All grant tokens for the same grant ID can be used interchangeably.

" + "smithy.api#documentation": "

A friendly name for the grant. Use this value to prevent the unintended creation of\n duplicate grants when retrying this request.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

When this value is absent, all CreateGrant requests result in a new grant\n with a unique GrantId even if all the supplied parameters are identical. This can\n result in unintended duplicates when you retry the CreateGrant request.

\n

When this value is present, you can retry a CreateGrant request with\n identical parameters; if the grant already exists, the original GrantId is\n returned without creating a new grant. Note that the returned grant token is unique with every\n CreateGrant request, even when a duplicate GrantId is returned.\n All grant tokens for the same grant ID can be used interchangeably.

" } } }, @@ -867,7 +889,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a unique customer managed KMS key in your Amazon Web Services account and Region.\n You can use a KMS key in cryptographic operations, such as encryption and signing. Some Amazon Web Services\n services let you use KMS keys that you create and manage to protect your service\n resources.

\n

A KMS key is a logical representation of a cryptographic key. In addition to the key\n material used in cryptographic operations, a KMS key includes metadata, such as the key ID,\n key policy, creation date, description, and key state. For details, see Managing keys in the\n Key Management Service Developer Guide\n

\n

Use the parameters of CreateKey to specify the type of KMS key, the source of\n its key material, its key policy, description, tags, and other properties.

\n \n

KMS has replaced the term customer master key (CMK) with KMS key and KMS key. The concept has not changed. To prevent breaking changes, KMS is keeping some variations of this term.

\n
\n

To create different types of KMS keys, use the following guidance:

\n
\n
Symmetric encryption KMS key
\n
\n

By default, CreateKey creates a symmetric encryption KMS key with key\n material that KMS generates. This is the basic and most widely used type of KMS key, and\n provides the best performance.

\n

To create a symmetric encryption KMS key, you don't need to specify any parameters.\n The default value for KeySpec, SYMMETRIC_DEFAULT, the default\n value for KeyUsage, ENCRYPT_DECRYPT, and the default value for\n Origin, AWS_KMS, create a symmetric encryption KMS key with\n KMS key material.

\n

If you need a key for basic encryption and decryption or you are creating a KMS key\n to protect your resources in an Amazon Web Services service, create a symmetric encryption KMS key.\n The key material in a symmetric encryption key never leaves KMS unencrypted. You can\n use a symmetric encryption KMS key to encrypt and decrypt data up to 4,096 bytes, but\n they are typically used to generate data keys and data keys pairs. For details, see\n GenerateDataKey and GenerateDataKeyPair.

\n

\n
\n
Asymmetric KMS keys
\n
\n

To create an asymmetric KMS key, use the KeySpec parameter to specify\n the type of key material in the KMS key. Then, use the KeyUsage parameter\n to determine whether the KMS key will be used to encrypt and decrypt or sign and verify.\n You can't change these properties after the KMS key is created.

\n

Asymmetric KMS keys contain an RSA key pair, Elliptic Curve (ECC) key pair, or an SM2 key pair (China Regions only). The private key in an asymmetric \n KMS key never leaves KMS unencrypted. However, you can use the GetPublicKey operation to download the public key\n so it can be used outside of KMS. KMS keys with RSA or SM2 key pairs can be used to encrypt or decrypt data or sign and verify messages (but not both). \n KMS keys with ECC key pairs can be used only to sign and verify messages. \n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

\n
\n
HMAC KMS key
\n
\n

To create an HMAC KMS key, set the KeySpec parameter to a key spec\n value for HMAC KMS keys. Then set the KeyUsage parameter to\n GENERATE_VERIFY_MAC. You must set the key usage even though\n GENERATE_VERIFY_MAC is the only valid key usage value for HMAC KMS keys.\n You can't change these properties after the KMS key is created.

\n

HMAC KMS keys are symmetric keys that never leave KMS unencrypted. You can use\n HMAC keys to generate (GenerateMac) and verify (VerifyMac) HMAC codes for messages up to 4096 bytes.

\n

HMAC KMS keys are not supported in all Amazon Web Services Regions. If you try to create an HMAC\n KMS key in an Amazon Web Services Region in which HMAC keys are not supported, the\n CreateKey operation returns an\n UnsupportedOperationException. For a list of Regions in which HMAC KMS keys\n are supported, see HMAC keys in\n KMS in the Key Management Service Developer Guide.

\n

\n
\n
Multi-Region primary keys
\n
Imported key material
\n
\n

To create a multi-Region primary key in the local Amazon Web Services Region,\n use the MultiRegion parameter with a value of True. To create\n a multi-Region replica key, that is, a KMS key with the same key ID\n and key material as a primary key, but in a different Amazon Web Services Region, use the ReplicateKey operation. To change a replica key to a primary key, and its\n primary key to a replica key, use the UpdatePrimaryRegion\n operation.

\n

You can create multi-Region KMS keys for all supported KMS key types: symmetric\n encryption KMS keys, HMAC KMS keys, asymmetric encryption KMS keys, and asymmetric\n signing KMS keys. You can also create multi-Region keys with imported key material.\n However, you can't create multi-Region keys in a custom key store.

\n

This operation supports multi-Region keys, an KMS feature that lets you create multiple\n interoperable KMS keys in different Amazon Web Services Regions. Because these KMS keys have the same key ID, key\n material, and other metadata, you can use them interchangeably to encrypt data in one Amazon Web Services Region and decrypt\n it in a different Amazon Web Services Region without re-encrypting the data or making a cross-Region call. For more information about multi-Region keys, see Multi-Region keys in KMS in the Key Management Service Developer Guide.

\n

\n
\n
\n

To import your own key material into a KMS key, begin by creating a symmetric\n encryption KMS key with no key material. To do this, use the Origin\n parameter of CreateKey with a value of EXTERNAL. Next, use\n GetParametersForImport operation to get a public key and import\n token, and use the public key to encrypt your key material. Then, use ImportKeyMaterial with your import token to import the key material. For\n step-by-step instructions, see Importing Key Material in the \n Key Management Service Developer Guide\n .

\n

This feature supports only symmetric encryption KMS keys, including multi-Region\n symmetric encryption KMS keys. You cannot import key material into any other type of KMS\n key.

\n

To create a multi-Region primary key with imported key material, use the\n Origin parameter of CreateKey with a value of\n EXTERNAL and the MultiRegion parameter with a value of\n True. To create replicas of the multi-Region primary key, use the ReplicateKey operation. For instructions, see Importing key material into\n multi-Region keys. For more information about multi-Region keys, see Multi-Region keys in KMS in the Key Management Service Developer Guide.

\n

\n
\n
Custom key store
\n
\n

A custom key store lets you protect your Amazon Web Services resources using keys in a backing key\n store that you own and manage. When you request a cryptographic operation with a KMS key\n in a custom key store, the operation is performed in the backing key store using its\n cryptographic keys.

\n

KMS supports CloudHSM key stores backed by an CloudHSM cluster and external key stores backed by an\n external key manager outside of Amazon Web Services. When you create a KMS key in an CloudHSM key store,\n KMS generates an encryption key in the CloudHSM cluster and associates it with the KMS\n key. When you create a KMS key in an external key store, you specify an existing\n encryption key in the external key manager.

\n \n

Some external key managers provide a simpler method for creating a KMS key in an\n external key store. For details, see your external key manager documentation.

\n
\n

Before you create a KMS key in a custom key store, the ConnectionState\n of the key store must be CONNECTED. To connect the custom key store, use\n the ConnectCustomKeyStore operation. To find the\n ConnectionState, use the DescribeCustomKeyStores\n operation.

\n

To create a KMS key in a custom key store, use the CustomKeyStoreId.\n Use the default KeySpec value, SYMMETRIC_DEFAULT, and the\n default KeyUsage value, ENCRYPT_DECRYPT to create a symmetric\n encryption key. No other key type is supported in a custom key store.

\n

To create a KMS key in an CloudHSM key store, use the\n Origin parameter with a value of AWS_CLOUDHSM. The CloudHSM\n cluster that is associated with the custom key store must have at least two active HSMs\n in different Availability Zones in the Amazon Web Services Region.

\n

To create a KMS key in an external key store, use the Origin parameter\n with a value of EXTERNAL_KEY_STORE and an XksKeyId parameter\n that identifies an existing external key.

\n \n

Some external key managers provide a simpler method for creating a KMS key in an\n external key store. For details, see your external key manager documentation.

\n
\n
\n
\n

\n Cross-account use: No. You cannot use this operation to\n create a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:CreateKey (IAM policy). To use the\n Tags parameter, kms:TagResource (IAM policy). For examples and information about related\n permissions, see Allow a user to create\n KMS keys in the Key Management Service Developer Guide.

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Creates a unique customer managed KMS key in your Amazon Web Services account and Region.\n You can use a KMS key in cryptographic operations, such as encryption and signing. Some Amazon Web Services\n services let you use KMS keys that you create and manage to protect your service\n resources.

\n

A KMS key is a logical representation of a cryptographic key. In addition to the key\n material used in cryptographic operations, a KMS key includes metadata, such as the key ID,\n key policy, creation date, description, and key state. For details, see Managing keys in the\n Key Management Service Developer Guide\n

\n

Use the parameters of CreateKey to specify the type of KMS key, the source of\n its key material, its key policy, description, tags, and other properties.

\n \n

KMS has replaced the term customer master key (CMK) with KMS key and KMS key. The concept has not changed. To prevent breaking changes, KMS is keeping some variations of this term.

\n
\n

To create different types of KMS keys, use the following guidance:

\n
\n
Symmetric encryption KMS key
\n
\n

By default, CreateKey creates a symmetric encryption KMS key with key\n material that KMS generates. This is the basic and most widely used type of KMS key, and\n provides the best performance.

\n

To create a symmetric encryption KMS key, you don't need to specify any parameters.\n The default value for KeySpec, SYMMETRIC_DEFAULT, the default\n value for KeyUsage, ENCRYPT_DECRYPT, and the default value for\n Origin, AWS_KMS, create a symmetric encryption KMS key with\n KMS key material.

\n

If you need a key for basic encryption and decryption or you are creating a KMS key\n to protect your resources in an Amazon Web Services service, create a symmetric encryption KMS key.\n The key material in a symmetric encryption key never leaves KMS unencrypted. You can\n use a symmetric encryption KMS key to encrypt and decrypt data up to 4,096 bytes, but\n they are typically used to generate data keys and data keys pairs. For details, see\n GenerateDataKey and GenerateDataKeyPair.

\n

\n
\n
Asymmetric KMS keys
\n
\n

To create an asymmetric KMS key, use the KeySpec parameter to specify\n the type of key material in the KMS key. Then, use the KeyUsage parameter\n to determine whether the KMS key will be used to encrypt and decrypt or sign and verify.\n You can't change these properties after the KMS key is created.

\n

Asymmetric KMS keys contain an RSA key pair, Elliptic Curve (ECC) key pair, or an SM2 key pair (China Regions only). The private key in an asymmetric \n KMS key never leaves KMS unencrypted. However, you can use the GetPublicKey operation to download the public key\n so it can be used outside of KMS. KMS keys with RSA or SM2 key pairs can be used to encrypt or decrypt data or sign and verify messages (but not both). \n KMS keys with ECC key pairs can be used only to sign and verify messages. \n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

\n
\n
HMAC KMS key
\n
\n

To create an HMAC KMS key, set the KeySpec parameter to a key spec\n value for HMAC KMS keys. Then set the KeyUsage parameter to\n GENERATE_VERIFY_MAC. You must set the key usage even though\n GENERATE_VERIFY_MAC is the only valid key usage value for HMAC KMS keys.\n You can't change these properties after the KMS key is created.

\n

HMAC KMS keys are symmetric keys that never leave KMS unencrypted. You can use\n HMAC keys to generate (GenerateMac) and verify (VerifyMac) HMAC codes for messages up to 4096 bytes.

\n

\n
\n
Multi-Region primary keys
\n
Imported key material
\n
\n

To create a multi-Region primary key in the local Amazon Web Services Region,\n use the MultiRegion parameter with a value of True. To create\n a multi-Region replica key, that is, a KMS key with the same key ID\n and key material as a primary key, but in a different Amazon Web Services Region, use the ReplicateKey operation. To change a replica key to a primary key, and its\n primary key to a replica key, use the UpdatePrimaryRegion\n operation.

\n

You can create multi-Region KMS keys for all supported KMS key types: symmetric\n encryption KMS keys, HMAC KMS keys, asymmetric encryption KMS keys, and asymmetric\n signing KMS keys. You can also create multi-Region keys with imported key material.\n However, you can't create multi-Region keys in a custom key store.

\n

This operation supports multi-Region keys, an KMS feature that lets you create multiple\n interoperable KMS keys in different Amazon Web Services Regions. Because these KMS keys have the same key ID, key\n material, and other metadata, you can use them interchangeably to encrypt data in one Amazon Web Services Region and decrypt\n it in a different Amazon Web Services Region without re-encrypting the data or making a cross-Region call. For more information about multi-Region keys, see Multi-Region keys in KMS in the Key Management Service Developer Guide.

\n

\n
\n
\n

To import your own key material into a KMS key, begin by creating a KMS key with no\n key material. To do this, use the Origin parameter of\n CreateKey with a value of EXTERNAL. Next, use GetParametersForImport operation to get a public key and import token. Use\n the wrapping public key to encrypt your key material. Then, use ImportKeyMaterial with your import token to import the key material. For step-by-step instructions, see\n Importing Key Material in the \n Key Management Service Developer Guide\n .

\n

You can import key material into KMS keys of all supported KMS key types: symmetric\n encryption KMS keys, HMAC KMS keys, asymmetric encryption KMS keys, and asymmetric\n signing KMS keys. You can also create multi-Region keys with imported key material.\n However, you can't import key material into a KMS key in a custom key store.

\n

To create a multi-Region primary key with imported key material, use the\n Origin parameter of CreateKey with a value of\n EXTERNAL and the MultiRegion parameter with a value of\n True. To create replicas of the multi-Region primary key, use the ReplicateKey operation. For instructions, see Importing key material into\n multi-Region keys. For more information about multi-Region keys, see Multi-Region keys in KMS in the Key Management Service Developer Guide.

\n

\n
\n
Custom key store
\n
\n

A custom key store lets you protect your Amazon Web Services resources using keys in a backing key\n store that you own and manage. When you request a cryptographic operation with a KMS key\n in a custom key store, the operation is performed in the backing key store using its\n cryptographic keys.

\n

KMS supports CloudHSM key stores backed by an CloudHSM cluster and external key stores backed by an\n external key manager outside of Amazon Web Services. When you create a KMS key in an CloudHSM key store,\n KMS generates an encryption key in the CloudHSM cluster and associates it with the KMS\n key. When you create a KMS key in an external key store, you specify an existing\n encryption key in the external key manager.

\n \n

Some external key managers provide a simpler method for creating a KMS key in an\n external key store. For details, see your external key manager documentation.

\n
\n

Before you create a KMS key in a custom key store, the ConnectionState\n of the key store must be CONNECTED. To connect the custom key store, use\n the ConnectCustomKeyStore operation. To find the\n ConnectionState, use the DescribeCustomKeyStores\n operation.

\n

To create a KMS key in a custom key store, use the CustomKeyStoreId.\n Use the default KeySpec value, SYMMETRIC_DEFAULT, and the\n default KeyUsage value, ENCRYPT_DECRYPT to create a symmetric\n encryption key. No other key type is supported in a custom key store.

\n

To create a KMS key in an CloudHSM key store, use the\n Origin parameter with a value of AWS_CLOUDHSM. The CloudHSM\n cluster that is associated with the custom key store must have at least two active HSMs\n in different Availability Zones in the Amazon Web Services Region.

\n

To create a KMS key in an external key store, use the Origin parameter\n with a value of EXTERNAL_KEY_STORE and an XksKeyId parameter\n that identifies an existing external key.

\n \n

Some external key managers provide a simpler method for creating a KMS key in an\n external key store. For details, see your external key manager documentation.

\n
\n
\n
\n

\n Cross-account use: No. You cannot use this operation to\n create a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:CreateKey (IAM policy). To use the\n Tags parameter, kms:TagResource (IAM policy). For examples and information about related\n permissions, see Allow a user to create\n KMS keys in the Key Management Service Developer Guide.

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#CreateKeyRequest": { @@ -882,7 +904,7 @@ "Description": { "target": "com.amazonaws.kms#DescriptionType", "traits": { - "smithy.api#documentation": "

A description of the KMS key.

\n

Use a description that helps you decide whether the KMS key is appropriate for a task. The\n default value is an empty string (no description).

\n

To set or change the description after the key is created, use UpdateKeyDescription.

" + "smithy.api#documentation": "

A description of the KMS key. Use a description that helps you decide whether the KMS key is appropriate for a task. The\n default value is an empty string (no description).

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

To set or change the description after the key is created, use UpdateKeyDescription.

" } }, "KeyUsage": { @@ -928,7 +950,7 @@ "Tags": { "target": "com.amazonaws.kms#TagList", "traits": { - "smithy.api#documentation": "

Assigns one or more tags to the KMS key. Use this parameter to tag the KMS key when it is\n created. To tag an existing KMS key, use the TagResource operation.

\n \n

Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

To use this parameter, you must have kms:TagResource permission in an IAM policy.

\n

Each tag consists of a tag key and a tag value. Both the tag key and the tag value are\n required, but the tag value can be an empty (null) string. You cannot have more than one tag\n on a KMS key with the same tag key. If you specify an existing tag key with a different tag\n value, KMS replaces the current tag value with the specified one.

\n

When you add tags to an Amazon Web Services resource, Amazon Web Services generates a cost allocation\n report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details,\n see Tagging Keys.

" + "smithy.api#documentation": "

Assigns one or more tags to the KMS key. Use this parameter to tag the KMS key when it is\n created. To tag an existing KMS key, use the TagResource operation.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n \n

Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

To use this parameter, you must have kms:TagResource permission in an IAM policy.

\n

Each tag consists of a tag key and a tag value. Both the tag key and the tag value are\n required, but the tag value can be an empty (null) string. You cannot have more than one tag\n on a KMS key with the same tag key. If you specify an existing tag key with a different tag\n value, KMS replaces the current tag value with the specified one.

\n

When you add tags to an Amazon Web Services resource, Amazon Web Services generates a cost allocation\n report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details,\n see Tagging Keys.

" } }, "MultiRegion": { @@ -1335,7 +1357,7 @@ } ], "traits": { - "smithy.api#documentation": "

Decrypts ciphertext that was encrypted by a KMS key using any of the following\n operations:

\n \n

You can use this operation to decrypt ciphertext that was encrypted under a symmetric\n encryption KMS key or an asymmetric encryption KMS key. When the KMS key is asymmetric, you\n must specify the KMS key and the encryption algorithm that was used to encrypt the ciphertext.\n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

The Decrypt operation also decrypts ciphertext that was encrypted outside of\n KMS by the public key in an KMS asymmetric KMS key. However, it cannot decrypt symmetric\n ciphertext produced by other libraries, such as the Amazon Web Services Encryption SDK or Amazon S3 client-side encryption.\n These libraries return a ciphertext format that is incompatible with KMS.

\n

If the ciphertext was encrypted under a symmetric encryption KMS key, the\n KeyId parameter is optional. KMS can get this information from metadata that\n it adds to the symmetric ciphertext blob. This feature adds durability to your implementation\n by ensuring that authorized users can decrypt ciphertext decades after it was encrypted, even\n if they've lost track of the key ID. However, specifying the KMS key is always recommended as\n a best practice. When you use the KeyId parameter to specify a KMS key, KMS\n only uses the KMS key you specify. If the ciphertext was encrypted under a different KMS key,\n the Decrypt operation fails. This practice ensures that you use the KMS key that\n you intend.

\n

Whenever possible, use key policies to give users permission to call the\n Decrypt operation on a particular KMS key, instead of using &IAM; policies.\n Otherwise, you might create an &IAM; policy that gives the user Decrypt\n permission on all KMS keys. This user could decrypt ciphertext that was encrypted by KMS keys\n in other accounts if the key policy for the cross-account KMS key permits it. If you must use\n an IAM policy for Decrypt permissions, limit the user to particular KMS keys or\n particular trusted accounts. For details, see Best practices for IAM\n policies in the Key Management Service Developer Guide.

\n

Applications in Amazon Web Services Nitro Enclaves can call this operation by using the Amazon Web Services Nitro Enclaves Development Kit. For information about the supporting parameters, see How Amazon Web Services Nitro Enclaves use KMS in the Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. If you use the KeyId\n parameter to identify a KMS key in a different Amazon Web Services account, specify the key ARN or the alias\n ARN of the KMS key.

\n

\n Required permissions: kms:Decrypt (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Decrypts ciphertext that was encrypted by a KMS key using any of the following\n operations:

\n \n

You can use this operation to decrypt ciphertext that was encrypted under a symmetric\n encryption KMS key or an asymmetric encryption KMS key. When the KMS key is asymmetric, you\n must specify the KMS key and the encryption algorithm that was used to encrypt the ciphertext.\n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

The Decrypt operation also decrypts ciphertext that was encrypted outside of\n KMS by the public key in an KMS asymmetric KMS key. However, it cannot decrypt symmetric\n ciphertext produced by other libraries, such as the Amazon Web Services Encryption SDK or Amazon S3 client-side encryption.\n These libraries return a ciphertext format that is incompatible with KMS.

\n

If the ciphertext was encrypted under a symmetric encryption KMS key, the\n KeyId parameter is optional. KMS can get this information from metadata that\n it adds to the symmetric ciphertext blob. This feature adds durability to your implementation\n by ensuring that authorized users can decrypt ciphertext decades after it was encrypted, even\n if they've lost track of the key ID. However, specifying the KMS key is always recommended as\n a best practice. When you use the KeyId parameter to specify a KMS key, KMS\n only uses the KMS key you specify. If the ciphertext was encrypted under a different KMS key,\n the Decrypt operation fails. This practice ensures that you use the KMS key that\n you intend.

\n

Whenever possible, use key policies to give users permission to call the\n Decrypt operation on a particular KMS key, instead of using &IAM; policies.\n Otherwise, you might create an &IAM; policy that gives the user Decrypt\n permission on all KMS keys. This user could decrypt ciphertext that was encrypted by KMS keys\n in other accounts if the key policy for the cross-account KMS key permits it. If you must use\n an IAM policy for Decrypt permissions, limit the user to particular KMS keys or\n particular trusted accounts. For details, see Best practices for IAM\n policies in the Key Management Service Developer Guide.

\n

\n Decrypt also supports Amazon Web Services Nitro Enclaves, which provide an\n isolated compute environment in Amazon EC2. To call Decrypt for a Nitro enclave, use\n the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK. Use the Recipient parameter to provide the\n attestation document for the enclave. Instead of the plaintext data, the response includes the\n plaintext data encrypted with the public key from the attestation document\n (CiphertextForRecipient).For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide..

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. If you use the KeyId\n parameter to identify a KMS key in a different Amazon Web Services account, specify the key ARN or the alias\n ARN of the KMS key.

\n

\n Required permissions: kms:Decrypt (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#DecryptRequest": { @@ -1371,6 +1393,12 @@ "traits": { "smithy.api#documentation": "

Specifies the encryption algorithm that will be used to decrypt the ciphertext. Specify\n the same algorithm that was used to encrypt the data. If you specify a different algorithm,\n the Decrypt operation fails.

\n

This parameter is required only when the ciphertext was encrypted under an asymmetric KMS\n key. The default value, SYMMETRIC_DEFAULT, represents the only supported\n algorithm that is valid for symmetric encryption KMS keys.

" } + }, + "Recipient": { + "target": "com.amazonaws.kms#RecipientInfo", + "traits": { + "smithy.api#documentation": "

A signed attestation document from\n an Amazon Web Services Nitro enclave and the encryption algorithm to use with the enclave's public key.\n The only valid encryption algorithm is RSAES_OAEP_SHA_256.

\n

This parameter only supports attestation documents for Amazon Web Services Nitro Enclaves. To include this\n parameter, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK.

\n

When you use this parameter, instead of returning the plaintext data, KMS encrypts the\n plaintext data with the public key in the attestation document, and returns the resulting\n ciphertext in the CiphertextForRecipient field in the response. This ciphertext\n can be decrypted only with the private key in the enclave. The Plaintext field in\n the response is null or empty.

\n

For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -1389,7 +1417,7 @@ "Plaintext": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

Decrypted plaintext data. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" + "smithy.api#documentation": "

Decrypted plaintext data. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

\n

If the response includes the CiphertextForRecipient field, the\n Plaintext field is null or empty.

" } }, "EncryptionAlgorithm": { @@ -1397,6 +1425,12 @@ "traits": { "smithy.api#documentation": "

The encryption algorithm that was used to decrypt the ciphertext.

" } + }, + "CiphertextForRecipient": { + "target": "com.amazonaws.kms#CiphertextType", + "traits": { + "smithy.api#documentation": "

The plaintext data encrypted with the public key in the attestation document.

\n

This field is included in the response only when the Recipient parameter in\n the request includes a valid attestation document from an Amazon Web Services Nitro enclave.\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -1521,7 +1555,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes key material that you previously imported. This operation makes the specified KMS\n key unusable. For more information about importing key material into KMS, see Importing Key Material\n in the Key Management Service Developer Guide.

\n

When the specified KMS key is in the PendingDeletion state, this operation\n does not change the KMS key's state. Otherwise, it changes the KMS key's state to\n PendingImport.

\n

After you delete key material, you can use ImportKeyMaterial to reimport\n the same key material into the KMS key.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:DeleteImportedKeyMaterial (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Deletes key material that was previously imported. This operation makes the specified KMS\n key temporarily unusable. To restore the usability of the KMS key, reimport the same key\n material. For more information about importing key material into KMS, see Importing Key Material\n in the Key Management Service Developer Guide.

\n

When the specified KMS key is in the PendingDeletion state, this operation\n does not change the KMS key's state. Otherwise, it changes the KMS key's state to\n PendingImport.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:DeleteImportedKeyMaterial (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#DeleteImportedKeyMaterialRequest": { @@ -2025,7 +2059,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used to encrypt the data.\n An encryption context is valid only for cryptographic operations with a symmetric encryption KMS key. The standard asymmetric encryption algorithms and HMAC algorithms that KMS uses do not support an encryption context.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used to encrypt the data.\n An encryption context is valid only for cryptographic operations with a symmetric encryption KMS key. The standard asymmetric encryption algorithms and HMAC algorithms that KMS uses do not support an encryption context.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "GrantTokens": { @@ -2193,7 +2227,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a unique symmetric data key for use outside of KMS. This operation returns a\n plaintext copy of the data key and a copy that is encrypted under a symmetric encryption KMS\n key that you specify. The bytes in the plaintext key are random; they are not related \n to the caller or the KMS key. You can use the plaintext key to encrypt your data outside of KMS \n and store the encrypted data key with the encrypted data.

\n

To generate a data key, specify the symmetric encryption KMS key that will be used to\n encrypt the data key. You cannot use an asymmetric KMS key to encrypt data keys. To get the\n type of your KMS key, use the DescribeKey operation.

\n

You must also specify the length of the data key. Use either the KeySpec or \n NumberOfBytes parameters (but not both). For 128-bit and 256-bit data keys, use \n the KeySpec parameter.

\n

To generate a 128-bit SM4 data key (China Regions only), specify a KeySpec value of\n AES_128 or a NumberOfBytes value of 16. The symmetric \n encryption key used in China Regions to encrypt your data key is an SM4 encryption key.

\n

To get only an encrypted copy of the data key, use GenerateDataKeyWithoutPlaintext. To generate an asymmetric data key pair, use\n the GenerateDataKeyPair or GenerateDataKeyPairWithoutPlaintext operation. To get a cryptographically secure\n random byte string, use GenerateRandom.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

Applications in Amazon Web Services Nitro Enclaves can call this operation by using the Amazon Web Services Nitro Enclaves Development Kit. For information about the supporting parameters, see How Amazon Web Services Nitro Enclaves use KMS in the Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n How to use your data key\n

\n

We recommend that you use the following pattern to encrypt data locally in your\n application. You can write your own code or use a client-side encryption library, such as the\n Amazon Web Services Encryption SDK, the\n Amazon DynamoDB Encryption Client,\n or Amazon S3\n client-side encryption to do these tasks for you.

\n

To encrypt data outside of KMS:

\n
    \n
  1. \n

    Use the GenerateDataKey operation to get a data key.

    \n
  2. \n
  3. \n

    Use the plaintext data key (in the Plaintext field of the response) to\n encrypt your data outside of KMS. Then erase the plaintext data key from memory.

    \n
  4. \n
  5. \n

    Store the encrypted data key (in the CiphertextBlob field of the\n response) with the encrypted data.

    \n
  6. \n
\n

To decrypt data outside of KMS:

\n
    \n
  1. \n

    Use the Decrypt operation to decrypt the encrypted data key. The\n operation returns a plaintext copy of the data key.

    \n
  2. \n
  3. \n

    Use the plaintext data key to decrypt data outside of KMS, then erase the plaintext\n data key from memory.

    \n
  4. \n
\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKey (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Returns a unique symmetric data key for use outside of KMS. This operation returns a\n plaintext copy of the data key and a copy that is encrypted under a symmetric encryption KMS\n key that you specify. The bytes in the plaintext key are random; they are not related \n to the caller or the KMS key. You can use the plaintext key to encrypt your data outside of KMS \n and store the encrypted data key with the encrypted data.

\n

To generate a data key, specify the symmetric encryption KMS key that will be used to\n encrypt the data key. You cannot use an asymmetric KMS key to encrypt data keys. To get the\n type of your KMS key, use the DescribeKey operation.

\n

You must also specify the length of the data key. Use either the KeySpec or \n NumberOfBytes parameters (but not both). For 128-bit and 256-bit data keys, use \n the KeySpec parameter.

\n

To generate a 128-bit SM4 data key (China Regions only), specify a KeySpec value of\n AES_128 or a NumberOfBytes value of 16. The symmetric \n encryption key used in China Regions to encrypt your data key is an SM4 encryption key.

\n

To get only an encrypted copy of the data key, use GenerateDataKeyWithoutPlaintext. To generate an asymmetric data key pair, use\n the GenerateDataKeyPair or GenerateDataKeyPairWithoutPlaintext operation. To get a cryptographically secure\n random byte string, use GenerateRandom.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

\n GenerateDataKey also supports Amazon Web Services Nitro Enclaves, which provide an\n isolated compute environment in Amazon EC2. To call GenerateDataKey for an Amazon Web Services Nitro\n enclave, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK. Use the Recipient parameter\n to provide the attestation document for the enclave. GenerateDataKey returns a\n copy of the data key encrypted under the specified KMS key, as usual. But instead of a\n plaintext copy of the data key, the response includes a copy of the data key encrypted under\n the public key from the attestation document (CiphertextForRecipient).\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide..

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n How to use your data key\n

\n

We recommend that you use the following pattern to encrypt data locally in your\n application. You can write your own code or use a client-side encryption library, such as the\n Amazon Web Services Encryption SDK, the\n Amazon DynamoDB Encryption Client,\n or Amazon S3\n client-side encryption to do these tasks for you.

\n

To encrypt data outside of KMS:

\n
    \n
  1. \n

    Use the GenerateDataKey operation to get a data key.

    \n
  2. \n
  3. \n

    Use the plaintext data key (in the Plaintext field of the response) to\n encrypt your data outside of KMS. Then erase the plaintext data key from memory.

    \n
  4. \n
  5. \n

    Store the encrypted data key (in the CiphertextBlob field of the\n response) with the encrypted data.

    \n
  6. \n
\n

To decrypt data outside of KMS:

\n
    \n
  1. \n

    Use the Decrypt operation to decrypt the encrypted data key. The\n operation returns a plaintext copy of the data key.

    \n
  2. \n
  3. \n

    Use the plaintext data key to decrypt data outside of KMS, then erase the plaintext\n data key from memory.

    \n
  4. \n
\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKey (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GenerateDataKeyPair": { @@ -2234,7 +2268,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a unique asymmetric data key pair for use outside of KMS. This operation returns\n a plaintext public key, a plaintext private key, and a copy of the private key that is\n encrypted under the symmetric encryption KMS key you specify. You can use the data key pair to\n perform asymmetric cryptography and implement digital signatures outside of KMS. The bytes\n in the keys are random; they not related to the caller or to the KMS key that is used to\n encrypt the private key.

\n

You can use the public key that GenerateDataKeyPair returns to encrypt data\n or verify a signature outside of KMS. Then, store the encrypted private key with the data.\n When you are ready to decrypt data or sign a message, you can use the Decrypt operation to decrypt the encrypted private key.

\n

To generate a data key pair, you must specify a symmetric encryption KMS key to encrypt\n the private key in a data key pair. You cannot use an asymmetric KMS key or a KMS key in a\n custom key store. To get the type and origin of your KMS key, use the DescribeKey operation.

\n

Use the KeyPairSpec parameter to choose an RSA or Elliptic Curve (ECC) data\n key pair. In China Regions, you can also choose an SM2 data key pair. KMS recommends that you use\n ECC key pairs for signing, and use RSA and SM2 key pairs for either encryption or signing, but not both.\n However, KMS cannot enforce any restrictions on the use of data key pairs outside of KMS.

\n

If you are using the data key pair to encrypt data, or for any operation where you don't\n immediately need a private key, consider using the GenerateDataKeyPairWithoutPlaintext operation.\n GenerateDataKeyPairWithoutPlaintext returns a plaintext public key and an\n encrypted private key, but omits the plaintext private key that you need only to decrypt\n ciphertext or sign a message. Later, when you need to decrypt the data or sign a message, use\n the Decrypt operation to decrypt the encrypted private key in the data key\n pair.

\n

\n GenerateDataKeyPair returns a unique data key pair for each request. The\n bytes in the keys are random; they are not related to the caller or the KMS key that is used\n to encrypt the private key. The public key is a DER-encoded X.509 SubjectPublicKeyInfo, as\n specified in RFC 5280. The private\n key is a DER-encoded PKCS8 PrivateKeyInfo, as specified in RFC 5958.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKeyPair (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Returns a unique asymmetric data key pair for use outside of KMS. This operation returns\n a plaintext public key, a plaintext private key, and a copy of the private key that is\n encrypted under the symmetric encryption KMS key you specify. You can use the data key pair to\n perform asymmetric cryptography and implement digital signatures outside of KMS. The bytes\n in the keys are random; they not related to the caller or to the KMS key that is used to\n encrypt the private key.

\n

You can use the public key that GenerateDataKeyPair returns to encrypt data\n or verify a signature outside of KMS. Then, store the encrypted private key with the data.\n When you are ready to decrypt data or sign a message, you can use the Decrypt operation to decrypt the encrypted private key.

\n

To generate a data key pair, you must specify a symmetric encryption KMS key to encrypt\n the private key in a data key pair. You cannot use an asymmetric KMS key or a KMS key in a\n custom key store. To get the type and origin of your KMS key, use the DescribeKey operation.

\n

Use the KeyPairSpec parameter to choose an RSA or Elliptic Curve (ECC) data\n key pair. In China Regions, you can also choose an SM2 data key pair. KMS recommends that you use\n ECC key pairs for signing, and use RSA and SM2 key pairs for either encryption or signing, but not both.\n However, KMS cannot enforce any restrictions on the use of data key pairs outside of KMS.

\n

If you are using the data key pair to encrypt data, or for any operation where you don't\n immediately need a private key, consider using the GenerateDataKeyPairWithoutPlaintext operation.\n GenerateDataKeyPairWithoutPlaintext returns a plaintext public key and an\n encrypted private key, but omits the plaintext private key that you need only to decrypt\n ciphertext or sign a message. Later, when you need to decrypt the data or sign a message, use\n the Decrypt operation to decrypt the encrypted private key in the data key\n pair.

\n

\n GenerateDataKeyPair returns a unique data key pair for each request. The\n bytes in the keys are random; they are not related to the caller or the KMS key that is used\n to encrypt the private key. The public key is a DER-encoded X.509 SubjectPublicKeyInfo, as\n specified in RFC 5280. The private\n key is a DER-encoded PKCS8 PrivateKeyInfo, as specified in RFC 5958.

\n

\n GenerateDataKeyPair also supports Amazon Web Services Nitro Enclaves, which provide an\n isolated compute environment in Amazon EC2. To call GenerateDataKeyPair for an Amazon Web Services Nitro\n enclave, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK. Use the Recipient parameter\n to provide the attestation document for the enclave. GenerateDataKeyPair returns the public data key and a\n copy of the private data key encrypted under the specified KMS key, as usual. But instead of a\n plaintext copy of the private data key (PrivateKeyPlaintext), the response includes a copy of the private data key encrypted under\n the public key from the attestation document (CiphertextForRecipient).\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide..

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKeyPair (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GenerateDataKeyPairRequest": { @@ -2243,7 +2277,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the private key in the\n data key pair.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the private key in the\n data key pair.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "KeyId": { @@ -2265,6 +2299,12 @@ "traits": { "smithy.api#documentation": "

A list of grant tokens.

\n

Use a grant token when your permission to call this operation comes from a new grant that has not yet achieved eventual consistency. For more information, see Grant token and Using a grant token in the\n Key Management Service Developer Guide.

" } + }, + "Recipient": { + "target": "com.amazonaws.kms#RecipientInfo", + "traits": { + "smithy.api#documentation": "

A signed attestation document from\n an Amazon Web Services Nitro enclave and the encryption algorithm to use with the enclave's public key.\n The only valid encryption algorithm is RSAES_OAEP_SHA_256.

\n

This parameter only supports attestation documents for Amazon Web Services Nitro Enclaves. To include this\n parameter, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK.

\n

When you use this parameter, instead of returning a plaintext copy of the private data key, KMS encrypts\n the plaintext private data key under the public key in the attestation document, and returns the\n resulting ciphertext in the CiphertextForRecipient field in the response. This\n ciphertext can be decrypted only with the private key in the enclave. The\n CiphertextBlob field in the response contains a copy of the private data key encrypted\n under the KMS key specified by the KeyId parameter. The PrivateKeyPlaintext\n field in the response is null or empty.

\n

For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2283,7 +2323,7 @@ "PrivateKeyPlaintext": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

The plaintext copy of the private key. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" + "smithy.api#documentation": "

The plaintext copy of the private key. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

\n

If the response includes the CiphertextForRecipient field, the\n PrivateKeyPlaintext field is null or empty.

" } }, "PublicKey": { @@ -2303,6 +2343,12 @@ "traits": { "smithy.api#documentation": "

The type of data key pair that was generated.

" } + }, + "CiphertextForRecipient": { + "target": "com.amazonaws.kms#CiphertextType", + "traits": { + "smithy.api#documentation": "

The plaintext private data key encrypted with the public key from the Nitro enclave. This ciphertext can\n be decrypted only by using a private key in the Nitro enclave.

\n

This field is included in the response only when the Recipient parameter in\n the request includes a valid attestation document from an Amazon Web Services Nitro enclave.\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2356,7 +2402,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the private key in the\n data key pair.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the private key in the\n data key pair.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "KeyId": { @@ -2429,7 +2475,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the data key.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the data key.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "NumberOfBytes": { @@ -2449,6 +2495,12 @@ "traits": { "smithy.api#documentation": "

A list of grant tokens.

\n

Use a grant token when your permission to call this operation comes from a new grant that has not yet achieved eventual consistency. For more information, see Grant token and Using a grant token in the\n Key Management Service Developer Guide.

" } + }, + "Recipient": { + "target": "com.amazonaws.kms#RecipientInfo", + "traits": { + "smithy.api#documentation": "

A signed attestation document from\n an Amazon Web Services Nitro enclave and the encryption algorithm to use with the enclave's public key.\n The only valid encryption algorithm is RSAES_OAEP_SHA_256.

\n

This parameter only supports attestation documents for Amazon Web Services Nitro Enclaves. To include this\n parameter, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK.

\n

When you use this parameter, instead of returning the plaintext data key, KMS encrypts\n the plaintext data key under the public key in the attestation document, and returns the\n resulting ciphertext in the CiphertextForRecipient field in the response. This\n ciphertext can be decrypted only with the private key in the enclave. The\n CiphertextBlob field in the response contains a copy of the data key encrypted\n under the KMS key specified by the KeyId parameter. The Plaintext\n field in the response is null or empty.

\n

For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2467,7 +2519,7 @@ "Plaintext": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

The plaintext data key. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded. Use this data key to encrypt your data outside of\n KMS. Then, remove it from memory as soon as possible.

" + "smithy.api#documentation": "

The plaintext data key. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded. Use this data key to encrypt your data outside of\n KMS. Then, remove it from memory as soon as possible.

\n

If the response includes the CiphertextForRecipient field, the\n Plaintext field is null or empty.

" } }, "KeyId": { @@ -2475,6 +2527,12 @@ "traits": { "smithy.api#documentation": "

The Amazon Resource Name (key ARN) of the KMS key that encrypted the data key.

" } + }, + "CiphertextForRecipient": { + "target": "com.amazonaws.kms#CiphertextType", + "traits": { + "smithy.api#documentation": "

The plaintext data key encrypted with the public key from the Nitro enclave. This ciphertext can\n be decrypted only by using a private key in the Nitro enclave.

\n

This field is included in the response only when the Recipient parameter in\n the request includes a valid attestation document from an Amazon Web Services Nitro enclave.\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2516,7 +2574,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a unique symmetric data key for use outside of KMS. This operation returns a\n data key that is encrypted under a symmetric encryption KMS key that you specify. The bytes in\n the key are random; they are not related to the caller or to the KMS key.

\n

\n GenerateDataKeyWithoutPlaintext is identical to the GenerateDataKey operation except that it does not return a plaintext copy of the\n data key.

\n

This operation is useful for systems that need to encrypt data at some point, but not\n immediately. When you need to encrypt the data, you call the Decrypt\n operation on the encrypted copy of the key.

\n

It's also useful in distributed systems with different levels of trust. For example, you\n might store encrypted data in containers. One component of your system creates new containers\n and stores an encrypted data key with each container. Then, a different component puts the\n data into the containers. That component first decrypts the data key, uses the plaintext data\n key to encrypt data, puts the encrypted data into the container, and then destroys the\n plaintext data key. In this system, the component that creates the containers never sees the\n plaintext data key.

\n

To request an asymmetric data key pair, use the GenerateDataKeyPair or\n GenerateDataKeyPairWithoutPlaintext operations.

\n

To generate a data key, you must specify the symmetric encryption KMS key that is used to\n encrypt the data key. You cannot use an asymmetric KMS key or a key in a custom key store to generate a data key. To get the\n type of your KMS key, use the DescribeKey operation.

\n

You must also specify the length of the data key. Use either the KeySpec or \n NumberOfBytes parameters (but not both). For 128-bit and 256-bit data keys, use \n the KeySpec parameter.

\n

To generate an SM4 data key (China Regions only), specify a KeySpec value of\n AES_128 or NumberOfBytes value of 128. The symmetric \n encryption key used in China Regions to encrypt your data key is an SM4 encryption key.

\n

If the operation succeeds, you will find the encrypted copy of the data key in the\n CiphertextBlob field.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKeyWithoutPlaintext (key\n policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Returns a unique symmetric data key for use outside of KMS. This operation returns a\n data key that is encrypted under a symmetric encryption KMS key that you specify. The bytes in\n the key are random; they are not related to the caller or to the KMS key.

\n

\n GenerateDataKeyWithoutPlaintext is identical to the GenerateDataKey operation except that it does not return a plaintext copy of the\n data key.

\n

This operation is useful for systems that need to encrypt data at some point, but not\n immediately. When you need to encrypt the data, you call the Decrypt\n operation on the encrypted copy of the key.

\n

It's also useful in distributed systems with different levels of trust. For example, you\n might store encrypted data in containers. One component of your system creates new containers\n and stores an encrypted data key with each container. Then, a different component puts the\n data into the containers. That component first decrypts the data key, uses the plaintext data\n key to encrypt data, puts the encrypted data into the container, and then destroys the\n plaintext data key. In this system, the component that creates the containers never sees the\n plaintext data key.

\n

To request an asymmetric data key pair, use the GenerateDataKeyPair or\n GenerateDataKeyPairWithoutPlaintext operations.

\n

To generate a data key, you must specify the symmetric encryption KMS key that is used to\n encrypt the data key. You cannot use an asymmetric KMS key or a key in a custom key store to generate a data key. To get the\n type of your KMS key, use the DescribeKey operation.

\n

You must also specify the length of the data key. Use either the KeySpec or \n NumberOfBytes parameters (but not both). For 128-bit and 256-bit data keys, use \n the KeySpec parameter.

\n

To generate an SM4 data key (China Regions only), specify a KeySpec value of\n AES_128 or NumberOfBytes value of 16. The symmetric\n encryption key used in China Regions to encrypt your data key is an SM4 encryption key.

\n

If the operation succeeds, you will find the encrypted copy of the data key in the\n CiphertextBlob field.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKeyWithoutPlaintext (key\n policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GenerateDataKeyWithoutPlaintextRequest": { @@ -2532,7 +2590,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the data key.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the data key.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "KeySpec": { @@ -2700,7 +2758,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a random byte string that is cryptographically secure.

\n

You must use the NumberOfBytes parameter to specify the length of the random\n byte string. There is no default value for string length.

\n

By default, the random byte string is generated in KMS. To generate the byte string in\n the CloudHSM cluster associated with an CloudHSM key store, use the CustomKeyStoreId\n parameter.

\n

Applications in Amazon Web Services Nitro Enclaves can call this operation by using the Amazon Web Services Nitro Enclaves Development Kit. For information about the supporting parameters, see How Amazon Web Services Nitro Enclaves use KMS in the Key Management Service Developer Guide.

\n

For more information about entropy and random number generation, see\n Key Management Service Cryptographic Details.

\n

\n Cross-account use: Not applicable.\n GenerateRandom does not use any account-specific resources, such as KMS\n keys.

\n

\n Required permissions: kms:GenerateRandom (IAM policy)

" + "smithy.api#documentation": "

Returns a random byte string that is cryptographically secure.

\n

You must use the NumberOfBytes parameter to specify the length of the random\n byte string. There is no default value for string length.

\n

By default, the random byte string is generated in KMS. To generate the byte string in\n the CloudHSM cluster associated with an CloudHSM key store, use the CustomKeyStoreId\n parameter.

\n

\n GenerateRandom also supports Amazon Web Services Nitro Enclaves, which provide an\n isolated compute environment in Amazon EC2. To call GenerateRandom for a Nitro\n enclave, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK. Use the Recipient parameter\n to provide the attestation document for the enclave. Instead of plaintext bytes, the response\n includes the plaintext bytes encrypted under the public key from the attestation document\n (CiphertextForRecipient).For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

\n

For more information about entropy and random number generation, see\n Key Management Service Cryptographic Details.

\n

\n Cross-account use: Not applicable.\n GenerateRandom does not use any account-specific resources, such as KMS\n keys.

\n

\n Required permissions: kms:GenerateRandom (IAM policy)

" } }, "com.amazonaws.kms#GenerateRandomRequest": { @@ -2715,7 +2773,13 @@ "CustomKeyStoreId": { "target": "com.amazonaws.kms#CustomKeyStoreIdType", "traits": { - "smithy.api#documentation": "

Generates the random byte string in the CloudHSM cluster that is associated with the\n specified CloudHSM key store. To find the ID of a custom key store, use the DescribeCustomKeyStores operation.

\n

External key store IDs are not valid for this parameter. If you specify the ID of an\n external key store, GenerateRandom throws an\n UnsupportedOperationException.

" + "smithy.api#documentation": "

Generates the random byte string in the CloudHSM cluster that is associated with the\n specified CloudHSM key store. To find the ID of a custom key store, use the DescribeCustomKeyStores operation.

\n

External key store IDs are not valid for this parameter. If you specify the ID of an\n external key store, GenerateRandom throws an\n UnsupportedOperationException.

" + } + }, + "Recipient": { + "target": "com.amazonaws.kms#RecipientInfo", + "traits": { + "smithy.api#documentation": "

A signed attestation document from\n an Amazon Web Services Nitro enclave and the encryption algorithm to use with the enclave's public key.\n The only valid encryption algorithm is RSAES_OAEP_SHA_256.

\n

This parameter only supports attestation documents for Amazon Web Services Nitro Enclaves. To include this\n parameter, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK.

\n

When you use this parameter, instead of returning plaintext bytes, KMS encrypts the\n plaintext bytes under the public key in the attestation document, and returns the resulting\n ciphertext in the CiphertextForRecipient field in the response. This ciphertext\n can be decrypted only with the private key in the enclave. The Plaintext field in\n the response is null or empty.

\n

For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" } } }, @@ -2729,7 +2793,13 @@ "Plaintext": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

The random byte string. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" + "smithy.api#documentation": "

The random byte string. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

\n

If the response includes the CiphertextForRecipient field, the\n Plaintext field is null or empty.

" + } + }, + "CiphertextForRecipient": { + "target": "com.amazonaws.kms#CiphertextType", + "traits": { + "smithy.api#documentation": "

The plaintext random bytes encrypted with the public key from the Nitro enclave. This ciphertext can\n be decrypted only by using a private key in the Nitro enclave.

\n

This field is included in the response only when the Recipient parameter in\n the request includes a valid attestation document from an Amazon Web Services Nitro enclave.\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" } } }, @@ -2893,7 +2963,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the items you need to import key material into a symmetric encryption KMS key. For\n more information about importing key material into KMS, see Importing key material in the\n Key Management Service Developer Guide.

\n

This operation returns a public key and an import token. Use the public key to encrypt the\n symmetric key material. Store the import token to send with a subsequent ImportKeyMaterial request.

\n

You must specify the key ID of the symmetric encryption KMS key into which you will import\n key material. The KMS key Origin must be EXTERNAL. You must also\n specify the wrapping algorithm and type of wrapping key (public key) that you will use to\n encrypt the key material. You cannot perform this operation on an asymmetric KMS key, an HMAC KMS key, or on any KMS key in a different Amazon Web Services account.

\n

To import key material, you must use the public key and import token from the same\n response. These items are valid for 24 hours. The expiration date and time appear in the\n GetParametersForImport response. You cannot use an expired token in an ImportKeyMaterial request. If your key and token expire, send another\n GetParametersForImport request.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:GetParametersForImport (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Returns the public key and an import token you need to import or reimport key material for\n a KMS key.

\n

By default, KMS keys are created with key material that KMS generates. This operation\n supports Importing key\n material, an advanced feature that lets you generate and import the cryptographic\n key material for a KMS key. For more information about importing key material into KMS, see\n Importing key\n material in the Key Management Service Developer Guide.

\n

Before calling GetParametersForImport, use the CreateKey\n operation with an Origin value of EXTERNAL to create a KMS key with\n no key material. You can import key material for a symmetric encryption KMS key, HMAC KMS key,\n asymmetric encryption KMS key, or asymmetric signing KMS key. You can also import key material\n into a multi-Region key of\n any supported type. However, you can't import key material into a KMS key in a custom key store. You can also use\n GetParametersForImport to get a public key and import token to reimport the original key material into a KMS key whose key material expired or was\n deleted.

\n

\n GetParametersForImport returns the items that you need to import your key\n material.

\n
    \n
  • \n

    The public key (or \"wrapping key\") of an RSA key pair that KMS generates.

    \n

    You will use this public key to encrypt (\"wrap\") your key material while it's in\n transit to KMS.

    \n
  • \n
  • \n

    A import token that ensures that KMS can decrypt your key material and associate it with the correct KMS key.

    \n
  • \n
\n

The public key and its import token are permanently linked and must be used together. Each\n public key and import token set is valid for 24 hours. The expiration date and time appear in\n the ParametersValidTo field in the GetParametersForImport response.\n You cannot use an expired public key or import token in an ImportKeyMaterial\n request. If your key and token expire, send another GetParametersForImport\n request.

\n

\n GetParametersForImport requires the following information:

\n
    \n
  • \n

    The key ID of the KMS key for which you are importing the key material.

    \n
  • \n
  • \n

    The key spec of the public key (\"wrapping key\") that you will use to encrypt your key\n material during import.

    \n
  • \n
  • \n

    The wrapping algorithm that you will use with the public key to encrypt your key\n material.

    \n
  • \n
\n

You can use the same or a different public key spec and wrapping algorithm each time you\n import or reimport the same key material.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:GetParametersForImport (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GetParametersForImportRequest": { @@ -2902,21 +2972,21 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

The identifier of the symmetric encryption KMS key into which you will import key\n material. The Origin of the KMS key must be EXTERNAL.

\n

Specify the key ID or key ARN of the KMS key.

\n

For example:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", + "smithy.api#documentation": "

The identifier of the KMS key that will be associated with the imported key material. The\n Origin of the KMS key must be EXTERNAL.

\n

All KMS key types are supported, including multi-Region keys. However, you cannot import\n key material into a KMS key in a custom key store.

\n

Specify the key ID or key ARN of the KMS key.

\n

For example:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", "smithy.api#required": {} } }, "WrappingAlgorithm": { "target": "com.amazonaws.kms#AlgorithmSpec", "traits": { - "smithy.api#documentation": "

The algorithm you will use to encrypt the key material before using the ImportKeyMaterial operation to import it. For more information, see Encrypt the\n key material in the Key Management Service Developer Guide.

\n \n

The RSAES_PKCS1_V1_5 wrapping algorithm is deprecated. We recommend that\n you begin using a different wrapping algorithm immediately. KMS will end support for\n RSAES_PKCS1_V1_5 by October 1, 2023 pursuant to cryptographic key management guidance from the National Institute of Standards\n and Technology (NIST).

\n
", + "smithy.api#documentation": "

The algorithm you will use with the RSA public key (PublicKey) in the\n response to protect your key material during import. For more information, see Select a wrapping algorithm in the Key Management Service Developer Guide.

\n

For RSA_AES wrapping algorithms, you encrypt your key material with an AES key that you\n generate, then encrypt your AES key with the RSA public key from KMS. For RSAES wrapping\n algorithms, you encrypt your key material directly with the RSA public key from KMS.

\n

The wrapping algorithms that you can use depend on the type of key material that you are\n importing. To import an RSA private key, you must use an RSA_AES wrapping algorithm.

\n
    \n
  • \n

    \n RSA_AES_KEY_WRAP_SHA_256 — Supported for wrapping RSA and ECC key\n material.

    \n
  • \n
  • \n

    \n RSA_AES_KEY_WRAP_SHA_1 — Supported for wrapping RSA and ECC key material.

    \n
  • \n
  • \n

    \n RSAES_OAEP_SHA_256 — Supported for all types of key material, except RSA key material (private key).

    \n

    You cannot use the RSAES_OAEP_SHA_256 wrapping algorithm with the RSA_2048 wrapping key spec to wrap \n ECC_NIST_P521 key material.

    \n
  • \n
  • \n

    \n RSAES_OAEP_SHA_1 — Supported for all types of key material, except RSA key material (private\n key).

    \n

    You cannot use the RSAES_OAEP_SHA_1 wrapping algorithm with the RSA_2048 wrapping key spec to wrap \n ECC_NIST_P521 key material.

    \n
  • \n
  • \n

    \n RSAES_PKCS1_V1_5 (Deprecated) — Supported only for symmetric encryption key\n material (and only in legacy mode).

    \n
  • \n
", "smithy.api#required": {} } }, "WrappingKeySpec": { "target": "com.amazonaws.kms#WrappingKeySpec", "traits": { - "smithy.api#documentation": "

The type of wrapping key (public key) to return in the response. Only 2048-bit RSA public\n keys are supported.

", + "smithy.api#documentation": "

The type of RSA public key to return in the response. You will use this wrapping key with\n the specified wrapping algorithm to protect your key material during import.

\n

Use the longest RSA wrapping key that is practical.

\n

You cannot use an RSA_2048 public key to directly wrap an ECC_NIST_P521 private key.\n Instead, use an RSA_AES wrapping algorithm or choose a longer RSA public key.

", "smithy.api#required": {} } } @@ -3351,7 +3421,7 @@ } ], "traits": { - "smithy.api#documentation": "

Imports key material into an existing symmetric encryption KMS key that was created\n without key material. After you successfully import key material into a KMS key, you can\n reimport the same key material into that KMS key, but you cannot import different\n key material.

\n

You cannot perform this operation on an asymmetric KMS key, an HMAC KMS key, or on any KMS key in a different Amazon Web Services account. For more information about creating KMS keys with no key material\n and then importing key material, see Importing Key Material in the\n Key Management Service Developer Guide.

\n

Before using this operation, call GetParametersForImport. Its response\n includes a public key and an import token. Use the public key to encrypt the key material.\n Then, submit the import token from the same GetParametersForImport\n response.

\n

When calling this operation, you must specify the following values:

\n
    \n
  • \n

    The key ID or key ARN of a KMS key with no key material. Its Origin must\n be EXTERNAL.

    \n

    To create a KMS key with no key material, call CreateKey and set the\n value of its Origin parameter to EXTERNAL. To get the\n Origin of a KMS key, call DescribeKey.)

    \n
  • \n
  • \n

    The encrypted key material. To get the public key to encrypt the key material, call\n GetParametersForImport.

    \n
  • \n
  • \n

    The import token that GetParametersForImport returned. You must use\n a public key and token from the same GetParametersForImport response.

    \n
  • \n
  • \n

    Whether the key material expires (ExpirationModel) and, if so, when\n (ValidTo). If you set an expiration date, on the specified date, KMS\n deletes the key material from the KMS key, making the KMS key unusable. To use the KMS key\n in cryptographic operations again, you must reimport the same key material. The only way\n to change the expiration model or expiration date is by reimporting the same key material\n and specifying a new expiration date.

    \n
  • \n
\n

When this operation is successful, the key state of the KMS key changes from\n PendingImport to Enabled, and you can use the KMS key.

\n

If this operation fails, use the exception to help determine the problem. If the error is\n related to the key material, the import token, or wrapping key, use GetParametersForImport to get a new public key and import token for the KMS key\n and repeat the import procedure. For help, see How To Import Key\n Material in the Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:ImportKeyMaterial (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Imports or reimports key material into an existing KMS key that was created without key\n material. ImportKeyMaterial also sets the expiration model and expiration date of\n the imported key material.

\n

By default, KMS keys are created with key material that KMS generates. This operation\n supports Importing key\n material, an advanced feature that lets you generate and import the cryptographic\n key material for a KMS key. For more information about importing key material into KMS, see\n Importing key\n material in the Key Management Service Developer Guide.

\n

After you successfully import key material into a KMS key, you can reimport\n the same key material into that KMS key, but you cannot import different key\n material. You might reimport key material to replace key material that expired or key material\n that you deleted. You might also reimport key material to change the expiration model or\n expiration date of the key material. Before reimporting key material, if necessary, call DeleteImportedKeyMaterial to delete the current imported key material.

\n

Each time you import key material into KMS, you can determine whether\n (ExpirationModel) and when (ValidTo) the key material expires. To\n change the expiration of your key material, you must import it again, either by calling\n ImportKeyMaterial or using the import features of the\n KMS console.

\n

Before calling ImportKeyMaterial:

\n
    \n
  • \n

    Create or identify a KMS key with no key material. The KMS key must have an\n Origin value of EXTERNAL, which indicates that the KMS key is\n designed for imported key material.

    \n

    To create an new KMS key for imported key material, call the CreateKey operation with an Origin value of EXTERNAL. You can create a\n symmetric encryption KMS key, HMAC KMS key, asymmetric encryption KMS key, or asymmetric\n signing KMS key. You can also import key material into a multi-Region key of any\n supported type. However, you can't import key material into a KMS key in a custom key store.

    \n
  • \n
  • \n

    Use the DescribeKey operation to verify that the\n KeyState of the KMS key is PendingImport, which indicates that\n the KMS key has no key material.

    \n

    If you are reimporting the same key material into an existing KMS key, you might need\n to call the DeleteImportedKeyMaterial to delete its existing key\n material.

    \n
  • \n
  • \n

    Call the GetParametersForImport operation to get a public key and\n import token set for importing key material.

    \n
  • \n
  • \n

    Use the public key in the GetParametersForImport response to encrypt\n your key material.

    \n
  • \n
\n

Then, in an ImportKeyMaterial request, you submit your encrypted key\n material and import token. When calling this operation, you must specify the following\n values:

\n
    \n
  • \n

    The key ID or key ARN of the KMS key to associate with the imported key material. Its\n Origin must be EXTERNAL and its KeyState must be\n PendingImport. You cannot perform this operation on a KMS key in a custom key store, or on a KMS\n key in a different Amazon Web Services account. To get the Origin and KeyState\n of a KMS key, call DescribeKey.

    \n
  • \n
  • \n

    The encrypted key material.

    \n
  • \n
  • \n

    The import token that GetParametersForImport returned. You must use\n a public key and token from the same GetParametersForImport response.

    \n
  • \n
  • \n

    Whether the key material expires (ExpirationModel) and, if so, when\n (ValidTo). For help with this choice, see Setting an expiration time in the Key Management Service Developer Guide.

    \n

    If you set an expiration date, KMS deletes the key material from the KMS key on the\n specified date, making the KMS key unusable. To use the KMS key in cryptographic\n operations again, you must reimport the same key material. However, you can delete and\n reimport the key material at any time, including before the key material expires. Each\n time you reimport, you can eliminate or reset the expiration time.

    \n
  • \n
\n

When this operation is successful, the key state of the KMS key changes from\n PendingImport to Enabled, and you can use the KMS key in\n cryptographic operations.

\n

If this operation fails, use the exception to help determine the problem. If the error is\n related to the key material, the import token, or wrapping key, use GetParametersForImport to get a new public key and import token for the KMS key\n and repeat the import procedure. For help, see How To Import Key\n Material in the Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:ImportKeyMaterial (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#ImportKeyMaterialRequest": { @@ -3360,7 +3430,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

The identifier of the symmetric encryption KMS key that receives the imported key\n material. This must be the same KMS key specified in the KeyID parameter of the\n corresponding GetParametersForImport request. The Origin of the\n KMS key must be EXTERNAL. You cannot perform this operation on an asymmetric KMS\n key, an HMAC KMS key, a KMS key in a custom key store, or on a KMS key in a different\n Amazon Web Services account

\n

Specify the key ID or key ARN of the KMS key.

\n

For example:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", + "smithy.api#documentation": "

The identifier of the KMS key that will be associated with the imported key material. This\n must be the same KMS key specified in the KeyID parameter of the corresponding\n GetParametersForImport request. The Origin of the KMS key\n must be EXTERNAL and its KeyState must be\n PendingImport.

\n

The KMS key can be a symmetric encryption KMS key, HMAC KMS key, asymmetric encryption KMS\n key, or asymmetric signing KMS key, including a multi-Region key of any supported\n type. You cannot perform this operation on a KMS key in a custom key store, or on a KMS key in\n a different Amazon Web Services account.

\n

Specify the key ID or key ARN of the KMS key.

\n

For example:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", "smithy.api#required": {} } }, @@ -3374,7 +3444,7 @@ "EncryptedKeyMaterial": { "target": "com.amazonaws.kms#CiphertextType", "traits": { - "smithy.api#documentation": "

The encrypted key material to import. The key material must be encrypted with the public\n wrapping key that GetParametersForImport returned, using the wrapping\n algorithm that you specified in the same GetParametersForImport request.

", + "smithy.api#documentation": "

The encrypted key material to import. The key material must be encrypted under the public\n wrapping key that GetParametersForImport returned, using the wrapping\n algorithm that you specified in the same GetParametersForImport request.

", "smithy.api#required": {} } }, @@ -3387,7 +3457,7 @@ "ExpirationModel": { "target": "com.amazonaws.kms#ExpirationModelType", "traits": { - "smithy.api#documentation": "

Specifies whether the key material expires. The default is\n KEY_MATERIAL_EXPIRES.

\n

When the value of ExpirationModel is KEY_MATERIAL_EXPIRES, you\n must specify a value for the ValidTo parameter. When value is\n KEY_MATERIAL_DOES_NOT_EXPIRE, you must omit the ValidTo\n parameter.

\n

You cannot change the ExpirationModel or ValidTo values for the\n current import after the request completes. To change either value, you must delete (DeleteImportedKeyMaterial) and reimport the key material.

" + "smithy.api#documentation": "

Specifies whether the key material expires. The default is\n KEY_MATERIAL_EXPIRES. For help with this choice, see Setting an expiration time in the Key Management Service Developer Guide.

\n

When the value of ExpirationModel is KEY_MATERIAL_EXPIRES, you\n must specify a value for the ValidTo parameter. When value is\n KEY_MATERIAL_DOES_NOT_EXPIRE, you must omit the ValidTo\n parameter.

\n

You cannot change the ExpirationModel or ValidTo values for the\n current import after the request completes. To change either value, you must reimport the key\n material.

" } } }, @@ -3657,6 +3727,17 @@ "smithy.api#httpError": 409 } }, + "com.amazonaws.kms#KeyEncryptionMechanism": { + "type": "enum", + "members": { + "RSAES_OAEP_SHA_256": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSAES_OAEP_SHA_256" + } + } + } + }, "com.amazonaws.kms#KeyIdType": { "type": "string", "traits": { @@ -5003,7 +5084,7 @@ "DestinationEncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies that encryption context to use when the reencrypting the data.

\n

A destination encryption context is valid only when the destination KMS key is a symmetric\n encryption KMS key. The standard ciphertext format for asymmetric KMS keys does not include\n fields for metadata.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies that encryption context to use when the reencrypting the data.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

A destination encryption context is valid only when the destination KMS key is a symmetric\n encryption KMS key. The standard ciphertext format for asymmetric KMS keys does not include\n fields for metadata.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "SourceEncryptionAlgorithm": { @@ -5067,6 +5148,26 @@ "smithy.api#output": {} } }, + "com.amazonaws.kms#RecipientInfo": { + "type": "structure", + "members": { + "KeyEncryptionAlgorithm": { + "target": "com.amazonaws.kms#KeyEncryptionMechanism", + "traits": { + "smithy.api#documentation": "

The encryption algorithm that KMS should use with the public key for an Amazon Web Services Nitro Enclave to encrypt plaintext \n values for the response. The only valid value is RSAES_OAEP_SHA_256.

" + } + }, + "AttestationDocument": { + "target": "com.amazonaws.kms#AttestationDocumentType", + "traits": { + "smithy.api#documentation": "

The attestation document for an Amazon Web Services Nitro Enclave. This document includes the enclave's public\n key.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains information about the party that receives the response from the API\n operation.

\n

This data type is designed to support Amazon Web Services Nitro Enclaves, which lets you create an isolated\n compute environment in Amazon EC2. For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } + }, "com.amazonaws.kms#RegionType": { "type": "string", "traits": { @@ -5154,13 +5255,13 @@ "Description": { "target": "com.amazonaws.kms#DescriptionType", "traits": { - "smithy.api#documentation": "

A description of the KMS key. The default value is an empty string (no\n description).

\n

The description is not a shared property of multi-Region keys. You can specify the same\n description or a different description for each key in a set of related multi-Region keys.\n KMS does not synchronize this property.

" + "smithy.api#documentation": "

A description of the KMS key. The default value is an empty string (no\n description).

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

The description is not a shared property of multi-Region keys. You can specify the same\n description or a different description for each key in a set of related multi-Region keys.\n KMS does not synchronize this property.

" } }, "Tags": { "target": "com.amazonaws.kms#TagList", "traits": { - "smithy.api#documentation": "

Assigns one or more tags to the replica key. Use this parameter to tag the KMS key when it\n is created. To tag an existing KMS key, use the TagResource\n operation.

\n \n

Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

To use this parameter, you must have kms:TagResource permission in an IAM policy.

\n

Tags are not a shared property of multi-Region keys. You can specify the same tags or\n different tags for each key in a set of related multi-Region keys. KMS does not synchronize\n this property.

\n

Each tag consists of a tag key and a tag value. Both the tag key and the tag value are\n required, but the tag value can be an empty (null) string. You cannot have more than one tag\n on a KMS key with the same tag key. If you specify an existing tag key with a different tag\n value, KMS replaces the current tag value with the specified one.

\n

When you add tags to an Amazon Web Services resource, Amazon Web Services generates a cost allocation\n report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details,\n see Tagging Keys.

" + "smithy.api#documentation": "

Assigns one or more tags to the replica key. Use this parameter to tag the KMS key when it\n is created. To tag an existing KMS key, use the TagResource\n operation.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n \n

Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

To use this parameter, you must have kms:TagResource permission in an IAM policy.

\n

Tags are not a shared property of multi-Region keys. You can specify the same tags or\n different tags for each key in a set of related multi-Region keys. KMS does not synchronize\n this property.

\n

Each tag consists of a tag key and a tag value. Both the tag key and the tag value are\n required, but the tag value can be an empty (null) string. You cannot have more than one tag\n on a KMS key with the same tag key. If you specify an existing tag key with a different tag\n value, KMS replaces the current tag value with the specified one.

\n

When you add tags to an Amazon Web Services resource, Amazon Web Services generates a cost allocation\n report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details,\n see Tagging Keys.

" } } }, @@ -5335,7 +5436,7 @@ } ], "traits": { - "smithy.api#documentation": "

Schedules the deletion of a KMS key. By default, KMS applies a waiting period of 30\n days, but you can specify a waiting period of 7-30 days. When this operation is successful,\n the key state of the KMS key changes to PendingDeletion and the key can't be used\n in any cryptographic operations. It remains in this state for the duration of the waiting\n period. Before the waiting period ends, you can use CancelKeyDeletion to\n cancel the deletion of the KMS key. After the waiting period ends, KMS deletes the KMS key,\n its key material, and all KMS data associated with it, including all aliases that refer to\n it.

\n \n

Deleting a KMS key is a destructive and potentially dangerous operation. When a KMS key\n is deleted, all data that was encrypted under the KMS key is unrecoverable. (The only\n exception is a multi-Region replica key.) To prevent the use of a KMS key without deleting\n it, use DisableKey.

\n
\n

You can schedule the deletion of a multi-Region primary key and its replica keys at any\n time. However, KMS will not delete a multi-Region primary key with existing replica keys. If\n you schedule the deletion of a primary key with replicas, its key state changes to\n PendingReplicaDeletion and it cannot be replicated or used in cryptographic\n operations. This status can continue indefinitely. When the last of its replicas keys is\n deleted (not just scheduled), the key state of the primary key changes to\n PendingDeletion and its waiting period (PendingWindowInDays)\n begins. For details, see Deleting multi-Region keys in the\n Key Management Service Developer Guide.

\n

When KMS deletes\n a KMS key from an CloudHSM key store, it makes a best effort to delete the associated\n key material from the associated CloudHSM cluster. However, you might need to manually delete\n the orphaned key material from the cluster and its backups. Deleting a KMS key from an\n external key store has no effect on the associated external key. However, for both\n types of custom key stores, deleting a KMS key is destructive and irreversible. You cannot\n decrypt ciphertext encrypted under the KMS key by using only its associated external key or\n CloudHSM key. Also, you cannot recreate a KMS key in an external key store by creating a new KMS\n key with the same key material.

\n

For more information about scheduling a KMS key for deletion, see Deleting KMS keys in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:ScheduleKeyDeletion (key\n policy)

\n

\n Related operations\n

\n " + "smithy.api#documentation": "

Schedules the deletion of a KMS key. By default, KMS applies a waiting period of 30\n days, but you can specify a waiting period of 7-30 days. When this operation is successful,\n the key state of the KMS key changes to PendingDeletion and the key can't be used\n in any cryptographic operations. It remains in this state for the duration of the waiting\n period. Before the waiting period ends, you can use CancelKeyDeletion to\n cancel the deletion of the KMS key. After the waiting period ends, KMS deletes the KMS key,\n its key material, and all KMS data associated with it, including all aliases that refer to\n it.

\n \n

Deleting a KMS key is a destructive and potentially dangerous operation. When a KMS key\n is deleted, all data that was encrypted under the KMS key is unrecoverable. (The only\n exception is a multi-Region replica\n key, or an asymmetric or HMAC KMS key with imported key material[BUGBUG-link to\n importing-keys-managing.html#import-delete-key.) To prevent the use of a KMS key without\n deleting it, use DisableKey.

\n
\n

You can schedule the deletion of a multi-Region primary key and its replica keys at any\n time. However, KMS will not delete a multi-Region primary key with existing replica keys. If\n you schedule the deletion of a primary key with replicas, its key state changes to\n PendingReplicaDeletion and it cannot be replicated or used in cryptographic\n operations. This status can continue indefinitely. When the last of its replicas keys is\n deleted (not just scheduled), the key state of the primary key changes to\n PendingDeletion and its waiting period (PendingWindowInDays)\n begins. For details, see Deleting multi-Region keys in the\n Key Management Service Developer Guide.

\n

When KMS deletes\n a KMS key from an CloudHSM key store, it makes a best effort to delete the associated\n key material from the associated CloudHSM cluster. However, you might need to manually delete\n the orphaned key material from the cluster and its backups. Deleting a KMS key from an\n external key store has no effect on the associated external key. However, for both\n types of custom key stores, deleting a KMS key is destructive and irreversible. You cannot\n decrypt ciphertext encrypted under the KMS key by using only its associated external key or\n CloudHSM key. Also, you cannot recreate a KMS key in an external key store by creating a new KMS\n key with the same key material.

\n

For more information about scheduling a KMS key for deletion, see Deleting KMS keys in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:ScheduleKeyDeletion (key\n policy)

\n

\n Related operations\n

\n " } }, "com.amazonaws.kms#ScheduleKeyDeletionRequest": { @@ -5351,7 +5452,7 @@ "PendingWindowInDays": { "target": "com.amazonaws.kms#PendingWindowInDaysType", "traits": { - "smithy.api#documentation": "

The waiting period, specified in number of days. After the waiting period ends, KMS\n deletes the KMS key.

\n

If the KMS key is a multi-Region primary key with replica keys, the waiting period begins\n when the last of its replica keys is deleted. Otherwise, the waiting period begins\n immediately.

\n

This value is optional. If you include a value, it must be between 7 and 30, inclusive. If\n you do not include a value, it defaults to 30.

" + "smithy.api#documentation": "

The waiting period, specified in number of days. After the waiting period ends, KMS\n deletes the KMS key.

\n

If the KMS key is a multi-Region primary key with replica keys, the waiting period begins\n when the last of its replica keys is deleted. Otherwise, the waiting period begins\n immediately.

\n

This value is optional. If you include a value, it must be between 7 and 30, inclusive. If\n you do not include a value, it defaults to 30. You can use the \n kms:ScheduleKeyDeletionPendingWindowInDays\n \n condition key to further constrain the values that principals can specify in the \n PendingWindowInDays parameter.

" } } }, @@ -5482,7 +5583,7 @@ "Signature": { "target": "com.amazonaws.kms#CiphertextType", "traits": { - "smithy.api#documentation": "

The cryptographic signature that was generated for the message.

\n
    \n
  • \n

    When used with the supported RSA signing algorithms, the encoding of this value is\n defined by PKCS #1 in RFC\n 8017.

    \n
  • \n
  • \n

    When used with the ECDSA_SHA_256, ECDSA_SHA_384, or\n ECDSA_SHA_512 signing algorithms, this value is a DER-encoded object as\n defined by ANS X9.62–2005 and RFC 3279 Section 2.2.3.\n This is the most commonly used signature format and is appropriate for most uses.\n

    \n
  • \n
\n

When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" + "smithy.api#documentation": "

The cryptographic signature that was generated for the message.

\n
    \n
  • \n

    When used with the supported RSA signing algorithms, the encoding of this value is\n defined by PKCS #1 in RFC\n 8017.

    \n
  • \n
  • \n

    When used with the ECDSA_SHA_256, ECDSA_SHA_384, or\n ECDSA_SHA_512 signing algorithms, this value is a DER-encoded object as\n defined by ANSI X9.62–2005 and RFC 3279 Section 2.2.3.\n This is the most commonly used signature format and is appropriate for most uses.\n

    \n
  • \n
\n

When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" } }, "SigningAlgorithm": { @@ -5586,7 +5687,7 @@ } }, "traits": { - "smithy.api#documentation": "

A key-value pair. A tag consists of a tag key and a tag value. Tag keys and tag values are\n both required, but tag values can be empty (null) strings.

\n

For information about the rules that apply to tag keys and tag values, see User-Defined Tag Restrictions in the Amazon Web Services Billing and Cost Management\n User Guide.

" + "smithy.api#documentation": "

A key-value pair. A tag consists of a tag key and a tag value. Tag keys and tag values are\n both required, but tag values can be empty (null) strings.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

For information about the rules that apply to tag keys and tag values, see User-Defined Tag Restrictions in the Amazon Web Services Billing and Cost Management\n User Guide.

" } }, "com.amazonaws.kms#TagException": { @@ -5672,7 +5773,7 @@ "Tags": { "target": "com.amazonaws.kms#TagList", "traits": { - "smithy.api#documentation": "

One or more tags.

\n

Each tag consists of a tag key and a tag value. The tag value can be an empty (null)\n string.

\n

You cannot have more than one tag on a KMS key with the same tag key. If you specify an\n existing tag key with a different tag value, KMS replaces the current tag value with the\n specified one.

", + "smithy.api#documentation": "

One or more tags. Each tag consists of a tag key and a tag value. The tag value can be an empty (null)\n string.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

You cannot have more than one tag on a KMS key with the same tag key. If you specify an\n existing tag key with a different tag value, KMS replaces the current tag value with the\n specified one.

", "smithy.api#required": {} } } @@ -6222,9 +6323,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -6235,9 +6336,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": true, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -6248,9 +6349,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -6261,9 +6362,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": true, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -6274,9 +6375,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -6287,9 +6388,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": true, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -6300,9 +6401,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -6313,9 +6414,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": true, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -6326,9 +6427,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -6339,9 +6440,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": true, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -6352,9 +6453,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -6365,9 +6466,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": true, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -6378,9 +6479,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -6391,9 +6492,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": true, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -6404,9 +6505,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -6417,9 +6518,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": true, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -6430,9 +6531,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -6443,9 +6544,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": true, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -6456,9 +6557,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -6469,9 +6570,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": true, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -6482,9 +6583,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -6495,9 +6596,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": true, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -6508,9 +6609,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -6521,9 +6622,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": true, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -6534,9 +6635,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -6547,9 +6648,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": true, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -6560,9 +6661,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -6573,9 +6674,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": true, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -6586,9 +6687,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -6599,9 +6700,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": true, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -6612,9 +6713,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -6625,9 +6726,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": true, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -6638,9 +6739,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -6651,9 +6752,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": true, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -6664,9 +6765,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -6677,9 +6778,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": true, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -6690,9 +6791,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -6703,9 +6804,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -6716,9 +6817,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -6729,9 +6830,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": true, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -6742,9 +6843,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -6755,9 +6856,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": true, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -6768,9 +6869,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -6781,9 +6882,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": true, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -6794,9 +6895,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -6807,9 +6908,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -6820,9 +6921,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -6833,9 +6934,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": false } }, { @@ -6846,9 +6947,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -6859,9 +6960,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -6872,9 +6973,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -6885,9 +6986,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -6898,9 +6999,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -6911,9 +7012,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": false, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -6924,9 +7025,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": true, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -6937,9 +7038,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -6950,9 +7051,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -6963,9 +7064,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -6976,9 +7077,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -6989,9 +7090,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", "UseFIPS": false, - "Region": "us-iso-west-1" + "UseDualStack": false } }, { @@ -7002,9 +7103,31 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", "UseFIPS": true, - "Region": "us-iso-west-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -7015,9 +7138,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false } }, { @@ -7028,9 +7151,31 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -7041,9 +7186,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -7055,8 +7200,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -7066,9 +7211,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -7078,11 +7223,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -7201,7 +7352,7 @@ "AliasName": { "target": "com.amazonaws.kms#AliasNameType", "traits": { - "smithy.api#documentation": "

Identifies the alias that is changing its KMS key. This value must begin with\n alias/ followed by the alias name, such as alias/ExampleAlias. You\n cannot use UpdateAlias to change the alias name.

", + "smithy.api#documentation": "

Identifies the alias that is changing its KMS key. This value must begin with\n alias/ followed by the alias name, such as alias/ExampleAlias. You\n cannot use UpdateAlias to change the alias name.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
", "smithy.api#required": {} } }, @@ -7295,7 +7446,7 @@ "NewCustomKeyStoreName": { "target": "com.amazonaws.kms#CustomKeyStoreNameType", "traits": { - "smithy.api#documentation": "

Changes the friendly name of the custom key store to the value that you specify. The\n custom key store name must be unique in the Amazon Web Services account.

\n

To change this value, an CloudHSM key store must be disconnected. An external key store can\n be connected or disconnected.

" + "smithy.api#documentation": "

Changes the friendly name of the custom key store to the value that you specify. The\n custom key store name must be unique in the Amazon Web Services account.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

To change this value, an CloudHSM key store must be disconnected. An external key store can\n be connected or disconnected.

" } }, "KeyStorePassword": { @@ -7394,7 +7545,7 @@ "Description": { "target": "com.amazonaws.kms#DescriptionType", "traits": { - "smithy.api#documentation": "

New description for the KMS key.

", + "smithy.api#documentation": "

New description for the KMS key.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
", "smithy.api#required": {} } } @@ -7688,6 +7839,18 @@ "traits": { "smithy.api#enumValue": "RSA_2048" } + }, + "RSA_3072": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSA_3072" + } + }, + "RSA_4096": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSA_4096" + } } } }, diff --git a/aws/sdk/aws-models/lambda.json b/aws/sdk/aws-models/lambda.json index 18759e228b..df1e421639 100644 --- a/aws/sdk/aws-models/lambda.json +++ b/aws/sdk/aws-models/lambda.json @@ -138,6 +138,9 @@ { "target": "com.amazonaws.lambda#InvokeAsync" }, + { + "target": "com.amazonaws.lambda#InvokeWithResponseStream" + }, { "target": "com.amazonaws.lambda#ListAliases" }, @@ -603,9 +606,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -616,9 +619,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": true } }, { @@ -629,9 +632,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -642,9 +645,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": true } }, { @@ -655,9 +658,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -668,9 +671,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": true } }, { @@ -681,9 +684,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -694,9 +697,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": true } }, { @@ -707,9 +710,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -720,9 +723,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": true } }, { @@ -733,9 +736,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -746,9 +749,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": true } }, { @@ -759,9 +762,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -772,9 +775,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": true } }, { @@ -785,9 +788,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -798,9 +801,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": true } }, { @@ -811,9 +814,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -824,9 +827,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": true } }, { @@ -837,9 +840,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -850,9 +853,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": true } }, { @@ -863,9 +866,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -876,9 +879,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": true } }, { @@ -889,9 +892,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -902,9 +905,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": true } }, { @@ -915,9 +918,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -928,9 +931,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": true } }, { @@ -941,9 +944,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -954,9 +957,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": true } }, { @@ -967,9 +970,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -980,9 +983,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": true } }, { @@ -993,9 +996,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -1006,9 +1009,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": true } }, { @@ -1019,9 +1022,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -1032,9 +1035,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": true } }, { @@ -1045,9 +1048,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -1058,9 +1061,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": true } }, { @@ -1071,9 +1074,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -1084,9 +1087,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -1097,9 +1100,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -1110,9 +1113,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -1123,9 +1126,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": true, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -1136,9 +1139,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": true } }, { @@ -1149,9 +1152,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -1162,9 +1165,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": true, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -1175,9 +1178,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": true } }, { @@ -1188,9 +1191,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -1201,9 +1204,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": true, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -1214,9 +1217,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": true } }, { @@ -1227,9 +1230,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -1240,9 +1243,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -1253,9 +1256,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -1266,9 +1269,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": false } }, { @@ -1279,9 +1282,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": true } }, { @@ -1292,9 +1295,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -1305,9 +1308,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -1318,9 +1321,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -1331,9 +1334,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -1344,9 +1347,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": false, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -1357,9 +1360,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": true, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -1370,9 +1373,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -1383,9 +1386,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -1396,9 +1399,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -1409,9 +1412,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", "UseFIPS": false, - "Region": "us-iso-west-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1422,9 +1436,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1435,9 +1460,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1448,9 +1484,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1461,9 +1508,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1475,8 +1522,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1486,9 +1533,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1498,11 +1545,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -2457,7 +2510,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a mapping between an event source and an Lambda function. Lambda reads items from the event source and invokes the function.

\n

For details about how to configure different event sources, see the following topics.

\n \n

The following error handling options are available only for stream sources (DynamoDB and Kinesis):

\n
    \n
  • \n

    \n BisectBatchOnFunctionError – If the function returns an error, split the batch in two and retry.

    \n
  • \n
  • \n

    \n DestinationConfig – Send discarded records to an Amazon SQS queue or Amazon SNS topic.

    \n
  • \n
  • \n

    \n MaximumRecordAgeInSeconds – Discard records older than the specified age. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires

    \n
  • \n
  • \n

    \n MaximumRetryAttempts – Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

    \n
  • \n
  • \n

    \n ParallelizationFactor – Process multiple batches from each shard concurrently.

    \n
  • \n
\n

For information about which configuration parameters apply to each event source, see the following topics.

\n ", + "smithy.api#documentation": "

Creates a mapping between an event source and an Lambda function. Lambda reads items from the event source and invokes the function.

\n

For details about how to configure different event sources, see the following topics.

\n \n

The following error handling options are available only for stream sources (DynamoDB and Kinesis):

\n
    \n
  • \n

    \n BisectBatchOnFunctionError – If the function returns an error, split the batch in two and retry.

    \n
  • \n
  • \n

    \n DestinationConfig – Send discarded records to an Amazon SQS queue or Amazon SNS topic.

    \n
  • \n
  • \n

    \n MaximumRecordAgeInSeconds – Discard records older than the specified age. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires

    \n
  • \n
  • \n

    \n MaximumRetryAttempts – Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

    \n
  • \n
  • \n

    \n ParallelizationFactor – Process multiple batches from each shard concurrently.

    \n
  • \n
\n

For information about which configuration parameters apply to each event source, see the following topics.

\n ", "smithy.api#http": { "method": "POST", "uri": "/2015-03-31/event-source-mappings", @@ -2471,7 +2524,7 @@ "EventSourceArn": { "target": "com.amazonaws.lambda#Arn", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the event source.

\n
    \n
  • \n

    \n Amazon Kinesis – The ARN of the data stream or a stream consumer.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – The ARN of the stream.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – The ARN of the queue.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – The ARN of the cluster.

    \n
  • \n
  • \n

    \n Amazon MQ – The ARN of the broker.

    \n
  • \n
" + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the event source.

\n
    \n
  • \n

    \n Amazon Kinesis – The ARN of the data stream or a stream consumer.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – The ARN of the stream.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – The ARN of the queue.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – The ARN of the cluster.

    \n
  • \n
  • \n

    \n Amazon MQ – The ARN of the broker.

    \n
  • \n
  • \n

    \n Amazon DocumentDB – The ARN of the DocumentDB change stream.

    \n
  • \n
" } }, "FunctionName": { @@ -2490,7 +2543,7 @@ "BatchSize": { "target": "com.amazonaws.lambda#BatchSize", "traits": { - "smithy.api#documentation": "

The maximum number of records in each batch that Lambda pulls from your stream or queue and sends to your function. Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation\n (6 MB).

\n
    \n
  • \n

    \n Amazon Kinesis – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Self-managed Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon MQ (ActiveMQ and RabbitMQ) – Default 100. Max 10,000.

    \n
  • \n
" + "smithy.api#documentation": "

The maximum number of records in each batch that Lambda pulls from your stream or queue and sends to your function. Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation\n (6 MB).

\n
    \n
  • \n

    \n Amazon Kinesis – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Self-managed Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon MQ (ActiveMQ and RabbitMQ) – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n DocumentDB – Default 100. Max 10,000.

    \n
  • \n
" } }, "FilterCriteria": { @@ -2502,19 +2555,19 @@ "MaximumBatchingWindowInSeconds": { "target": "com.amazonaws.lambda#MaximumBatchingWindowInSeconds", "traits": { - "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, and Amazon MQ event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" + "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, Amazon MQ, and DocumentDB event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" } }, "ParallelizationFactor": { "target": "com.amazonaws.lambda#ParallelizationFactor", "traits": { - "smithy.api#documentation": "

(Streams only) The number of batches to process from each shard concurrently.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The number of batches to process from each shard concurrently.

" } }, "StartingPosition": { "target": "com.amazonaws.lambda#EventSourcePosition", "traits": { - "smithy.api#documentation": "

The position in a stream from which to start reading. Required for Amazon Kinesis, Amazon\n DynamoDB, and Amazon MSK Streams sources. AT_TIMESTAMP is supported only for\n Amazon Kinesis streams.

" + "smithy.api#documentation": "

The position in a stream from which to start reading. Required for Amazon Kinesis, Amazon\n DynamoDB, and Amazon MSK Streams sources. AT_TIMESTAMP is supported only for\n Amazon Kinesis streams and Amazon DocumentDB.

" } }, "StartingPositionTimestamp": { @@ -2526,31 +2579,31 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

(Streams only) An Amazon SQS queue or Amazon SNS topic destination for discarded records.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) A standard Amazon SQS queue or standard Amazon SNS topic destination for discarded records.

" } }, "MaximumRecordAgeInSeconds": { "target": "com.amazonaws.lambda#MaximumRecordAgeInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records older than the specified age. The default value is infinite (-1).

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records older than the specified age. The default value is infinite (-1).

" } }, "BisectBatchOnFunctionError": { "target": "com.amazonaws.lambda#BisectBatchOnFunctionError", "traits": { - "smithy.api#documentation": "

(Streams only) If the function returns an error, split the batch in two and retry.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) If the function returns an error, split the batch in two and retry.

" } }, "MaximumRetryAttempts": { "target": "com.amazonaws.lambda#MaximumRetryAttemptsEventSourceMapping", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

" } }, "TumblingWindowInSeconds": { "target": "com.amazonaws.lambda#TumblingWindowInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) The duration in seconds of a processing window. The range is between 1 second and 900 seconds.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The duration in seconds of a processing window for DynamoDB and Kinesis Streams event sources. A value of 0 seconds indicates no tumbling window.

" } }, "Topics": { @@ -2580,7 +2633,7 @@ "FunctionResponseTypes": { "target": "com.amazonaws.lambda#FunctionResponseTypeList", "traits": { - "smithy.api#documentation": "

(Streams and Amazon SQS) A list of current response type enums applied to the event source mapping.

" + "smithy.api#documentation": "

(Kinesis, DynamoDB Streams, and Amazon SQS) A list of current response type enums applied to the event source mapping.

" } }, "AmazonManagedKafkaEventSourceConfig": { @@ -2746,7 +2799,7 @@ "KMSKeyArn": { "target": "com.amazonaws.lambda#KMSKeyArn", "traits": { - "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) customer managed key that's used to encrypt your function's environment variables. When Lambda SnapStart is activated, this key is also used to encrypt your function's snapshot. If you don't provide a customer managed key, Lambda uses a default service key.

" + "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) customer managed key that's used to encrypt your function's \nenvironment variables. When \nLambda SnapStart is activated, Lambda also uses \nthis key is to encrypt your function's snapshot. If you deploy your function using a container image, Lambda also uses this key to \nencrypt your function when it's deployed. Note that this is not the same key that's used to protect your container image in the Amazon Elastic Container Registry (Amazon ECR).\nIf you don't provide a customer managed key, Lambda uses a default service key.

" } }, "TracingConfig": { @@ -2872,6 +2925,12 @@ "traits": { "smithy.api#documentation": "

The cross-origin resource sharing (CORS) settings\n for your function URL.

" } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -2914,6 +2973,12 @@ "smithy.api#documentation": "

When the function URL was created, in ISO-8601 format (YYYY-MM-DDThh:mm:ss.sTZD).

", "smithy.api#required": {} } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -3847,7 +3912,7 @@ "StartingPosition": { "target": "com.amazonaws.lambda#EventSourcePosition", "traits": { - "smithy.api#documentation": "

The position in a stream from which to start reading. Required for Amazon Kinesis, Amazon DynamoDB, and Amazon MSK stream sources. AT_TIMESTAMP is supported only for Amazon Kinesis\n streams.

" + "smithy.api#documentation": "

The position in a stream from which to start reading. Required for Amazon Kinesis, Amazon DynamoDB, and Amazon MSK stream sources. AT_TIMESTAMP is supported only for Amazon Kinesis\n streams and Amazon DocumentDB.

" } }, "StartingPositionTimestamp": { @@ -3865,13 +3930,13 @@ "MaximumBatchingWindowInSeconds": { "target": "com.amazonaws.lambda#MaximumBatchingWindowInSeconds", "traits": { - "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, and Amazon MQ event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" + "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, Amazon MQ, and DocumentDB event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" } }, "ParallelizationFactor": { "target": "com.amazonaws.lambda#ParallelizationFactor", "traits": { - "smithy.api#documentation": "

(Streams only) The number of batches to process concurrently from each shard. The default value is 1.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The number of batches to process concurrently from each shard. The default value is 1.

" } }, "EventSourceArn": { @@ -3919,7 +3984,7 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

(Streams only) An Amazon SQS queue or Amazon SNS topic destination for discarded records.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) An Amazon SQS queue or Amazon SNS topic destination for discarded records.

" } }, "Topics": { @@ -3949,31 +4014,31 @@ "MaximumRecordAgeInSeconds": { "target": "com.amazonaws.lambda#MaximumRecordAgeInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records older than the specified age. The default value is -1,\nwhich sets the maximum age to infinite. When the value is set to infinite, Lambda never discards old records.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records older than the specified age. The default value is -1,\nwhich sets the maximum age to infinite. When the value is set to infinite, Lambda never discards old records.

\n \n

The minimum valid value for maximum record age is 60s. Although values less than 60 and greater than -1 fall within the parameter's absolute range, they are not allowed

\n
" } }, "BisectBatchOnFunctionError": { "target": "com.amazonaws.lambda#BisectBatchOnFunctionError", "traits": { - "smithy.api#documentation": "

(Streams only) If the function returns an error, split the batch in two and retry. The default value is false.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) If the function returns an error, split the batch in two and retry. The default value is false.

" } }, "MaximumRetryAttempts": { "target": "com.amazonaws.lambda#MaximumRetryAttemptsEventSourceMapping", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records after the specified number of retries. The default value is -1,\nwhich sets the maximum number of retries to infinite. When MaximumRetryAttempts is infinite, Lambda retries failed records until the record expires in the event source.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records after the specified number of retries. The default value is -1,\nwhich sets the maximum number of retries to infinite. When MaximumRetryAttempts is infinite, Lambda retries failed records until the record expires in the event source.

" } }, "TumblingWindowInSeconds": { "target": "com.amazonaws.lambda#TumblingWindowInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) The duration in seconds of a processing window. The range is 1–900 seconds.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The duration in seconds of a processing window for DynamoDB and Kinesis Streams event sources. A value of 0 seconds indicates no tumbling window.

" } }, "FunctionResponseTypes": { "target": "com.amazonaws.lambda#FunctionResponseTypeList", "traits": { - "smithy.api#documentation": "

(Streams and Amazon SQS) A list of current response type enums applied to the event source mapping.

" + "smithy.api#documentation": "

(Kinesis, DynamoDB Streams, and Amazon SQS) A list of current response type enums applied to the event source mapping.

" } }, "AmazonManagedKafkaEventSourceConfig": { @@ -4239,7 +4304,7 @@ "Runtime": { "target": "com.amazonaws.lambda#Runtime", "traits": { - "smithy.api#documentation": "

The runtime environment for the Lambda function.

" + "smithy.api#documentation": "

The identifier of the function's runtime. Runtime is required if the deployment package is a .zip file archive.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "Role": { @@ -4470,7 +4535,7 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of an SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of an SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" + "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of a standard SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of a standard SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" } } } @@ -4589,6 +4654,12 @@ "smithy.api#documentation": "

The type of authentication that your function URL uses. Set to AWS_IAM if you want to restrict access to authenticated\n users only. Set to NONE if you want to bypass IAM authentication to create a public endpoint. For more information,\n see Security and auth model for Lambda function URLs.

", "smithy.api#required": {} } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function\n using the Invoke API operation. Invocation results are available when the\n payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available.\n Lambda invokes your function using the InvokeWithResponseStream\n API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -5457,6 +5528,12 @@ "smithy.api#documentation": "

When the function URL configuration was last updated, in ISO-8601 format (YYYY-MM-DDThh:mm:ss.sTZD).

", "smithy.api#required": {} } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -5685,7 +5762,7 @@ "CompatibleRuntimes": { "target": "com.amazonaws.lambda#CompatibleRuntimes", "traits": { - "smithy.api#documentation": "

The layer's compatible runtimes.

" + "smithy.api#documentation": "

The layer's compatible runtimes.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "LicenseInfo": { @@ -6494,6 +6571,279 @@ "smithy.api#output": {} } }, + "com.amazonaws.lambda#InvokeMode": { + "type": "enum", + "members": { + "BUFFERED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BUFFERED" + } + }, + "RESPONSE_STREAM": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESPONSE_STREAM" + } + } + } + }, + "com.amazonaws.lambda#InvokeResponseStreamUpdate": { + "type": "structure", + "members": { + "Payload": { + "target": "com.amazonaws.lambda#Blob", + "traits": { + "smithy.api#documentation": "

Data returned by your Lambda function.

", + "smithy.api#eventPayload": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A chunk of the streamed response payload.

" + } + }, + "com.amazonaws.lambda#InvokeWithResponseStream": { + "type": "operation", + "input": { + "target": "com.amazonaws.lambda#InvokeWithResponseStreamRequest" + }, + "output": { + "target": "com.amazonaws.lambda#InvokeWithResponseStreamResponse" + }, + "errors": [ + { + "target": "com.amazonaws.lambda#EC2AccessDeniedException" + }, + { + "target": "com.amazonaws.lambda#EC2ThrottledException" + }, + { + "target": "com.amazonaws.lambda#EC2UnexpectedException" + }, + { + "target": "com.amazonaws.lambda#EFSIOException" + }, + { + "target": "com.amazonaws.lambda#EFSMountConnectivityException" + }, + { + "target": "com.amazonaws.lambda#EFSMountFailureException" + }, + { + "target": "com.amazonaws.lambda#EFSMountTimeoutException" + }, + { + "target": "com.amazonaws.lambda#ENILimitReachedException" + }, + { + "target": "com.amazonaws.lambda#InvalidParameterValueException" + }, + { + "target": "com.amazonaws.lambda#InvalidRequestContentException" + }, + { + "target": "com.amazonaws.lambda#InvalidRuntimeException" + }, + { + "target": "com.amazonaws.lambda#InvalidSecurityGroupIDException" + }, + { + "target": "com.amazonaws.lambda#InvalidSubnetIDException" + }, + { + "target": "com.amazonaws.lambda#InvalidZipFileException" + }, + { + "target": "com.amazonaws.lambda#KMSAccessDeniedException" + }, + { + "target": "com.amazonaws.lambda#KMSDisabledException" + }, + { + "target": "com.amazonaws.lambda#KMSInvalidStateException" + }, + { + "target": "com.amazonaws.lambda#KMSNotFoundException" + }, + { + "target": "com.amazonaws.lambda#RequestTooLargeException" + }, + { + "target": "com.amazonaws.lambda#ResourceConflictException" + }, + { + "target": "com.amazonaws.lambda#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.lambda#ResourceNotReadyException" + }, + { + "target": "com.amazonaws.lambda#ServiceException" + }, + { + "target": "com.amazonaws.lambda#SnapStartException" + }, + { + "target": "com.amazonaws.lambda#SnapStartNotReadyException" + }, + { + "target": "com.amazonaws.lambda#SnapStartTimeoutException" + }, + { + "target": "com.amazonaws.lambda#SubnetIPAddressLimitReachedException" + }, + { + "target": "com.amazonaws.lambda#TooManyRequestsException" + }, + { + "target": "com.amazonaws.lambda#UnsupportedMediaTypeException" + } + ], + "traits": { + "smithy.api#documentation": "

Configure your Lambda functions to stream response payloads back to clients. For more information, see Configuring a Lambda function to stream responses.

\n

This operation requires permission for the lambda:InvokeFunction action. For details on how to set up\n permissions for cross-account invocations, see Granting function\n access to other accounts.

", + "smithy.api#http": { + "method": "POST", + "uri": "/2021-11-15/functions/{FunctionName}/response-streaming-invocations", + "code": 200 + } + } + }, + "com.amazonaws.lambda#InvokeWithResponseStreamCompleteEvent": { + "type": "structure", + "members": { + "ErrorCode": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

An error code.

" + } + }, + "ErrorDetails": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

The details of any returned error.

" + } + }, + "LogResult": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

The last 4 KB of the execution log, which is base64-encoded.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A response confirming that the event stream is complete.

" + } + }, + "com.amazonaws.lambda#InvokeWithResponseStreamRequest": { + "type": "structure", + "members": { + "FunctionName": { + "target": "com.amazonaws.lambda#NamespacedFunctionName", + "traits": { + "smithy.api#documentation": "

The name of the Lambda function.

\n

\n Name formats\n

\n
    \n
  • \n

    \n Function namemy-function.

    \n
  • \n
  • \n

    \n Function ARNarn:aws:lambda:us-west-2:123456789012:function:my-function.

    \n
  • \n
  • \n

    \n Partial ARN123456789012:function:my-function.

    \n
  • \n
\n

The length constraint applies only to the full ARN. If you specify only the function name, it is limited to 64\n characters in length.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "InvocationType": { + "target": "com.amazonaws.lambda#ResponseStreamingInvocationType", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n RequestResponse (default) – Invoke the function synchronously. Keep the\n connection open until the function returns a response or times out. The API operation\n response includes the function response and additional data.

    \n
  • \n
  • \n

    \n DryRun – Validate parameter values and verify that the IAM user or role has permission to invoke\n the function.

    \n
  • \n
", + "smithy.api#httpHeader": "X-Amz-Invocation-Type" + } + }, + "LogType": { + "target": "com.amazonaws.lambda#LogType", + "traits": { + "smithy.api#documentation": "

Set to Tail to include the execution log in the response. Applies to synchronously invoked functions only.

", + "smithy.api#httpHeader": "X-Amz-Log-Type" + } + }, + "ClientContext": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

Up to 3,583 bytes of base64-encoded data about the invoking client to pass to the function in the context\n object.

", + "smithy.api#httpHeader": "X-Amz-Client-Context" + } + }, + "Qualifier": { + "target": "com.amazonaws.lambda#Qualifier", + "traits": { + "smithy.api#documentation": "

The alias name.

", + "smithy.api#httpQuery": "Qualifier" + } + }, + "Payload": { + "target": "com.amazonaws.lambda#Blob", + "traits": { + "smithy.api#documentation": "

The JSON that you want to provide to your Lambda function as input.

\n

You can enter the JSON directly. For example, --payload '{ \"key\": \"value\" }'. You can also\n specify a file path. For example, --payload file://payload.json.

", + "smithy.api#httpPayload": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.lambda#InvokeWithResponseStreamResponse": { + "type": "structure", + "members": { + "StatusCode": { + "target": "com.amazonaws.lambda#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

For a successful request, the HTTP status code is in the 200 range. For the\n RequestResponse invocation type, this status code is 200. For the DryRun\n invocation type, this status code is 204.

", + "smithy.api#httpResponseCode": {} + } + }, + "ExecutedVersion": { + "target": "com.amazonaws.lambda#Version", + "traits": { + "smithy.api#documentation": "

The version of the function that executed. When you invoke a function with an alias, this\n indicates which version the alias resolved to.

", + "smithy.api#httpHeader": "X-Amz-Executed-Version" + } + }, + "EventStream": { + "target": "com.amazonaws.lambda#InvokeWithResponseStreamResponseEvent", + "traits": { + "smithy.api#documentation": "

The stream of response payloads.

", + "smithy.api#httpPayload": {} + } + }, + "ResponseStreamContentType": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

The type of data the stream is returning.

", + "smithy.api#httpHeader": "Content-Type" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.lambda#InvokeWithResponseStreamResponseEvent": { + "type": "union", + "members": { + "PayloadChunk": { + "target": "com.amazonaws.lambda#InvokeResponseStreamUpdate", + "traits": { + "smithy.api#documentation": "

A chunk of the streamed response payload.

" + } + }, + "InvokeComplete": { + "target": "com.amazonaws.lambda#InvokeWithResponseStreamCompleteEvent", + "traits": { + "smithy.api#documentation": "

An object that's returned when the stream has ended and all the payload chunks have been\n returned.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An object that includes a chunk of the response payload. When the stream has ended, Lambda includes a InvokeComplete object.

", + "smithy.api#streaming": {} + } + }, "com.amazonaws.lambda#KMSAccessDeniedException": { "type": "structure", "members": { @@ -6920,7 +7270,7 @@ "CompatibleRuntimes": { "target": "com.amazonaws.lambda#CompatibleRuntimes", "traits": { - "smithy.api#documentation": "

The layer's compatible runtimes.

" + "smithy.api#documentation": "

The layer's compatible runtimes.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "LicenseInfo": { @@ -7197,7 +7547,7 @@ "EventSourceArn": { "target": "com.amazonaws.lambda#Arn", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the event source.

\n
    \n
  • \n

    \n Amazon Kinesis – The ARN of the data stream or a stream consumer.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – The ARN of the stream.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – The ARN of the queue.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – The ARN of the cluster.

    \n
  • \n
  • \n

    \n Amazon MQ – The ARN of the broker.

    \n
  • \n
", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the event source.

\n
    \n
  • \n

    \n Amazon Kinesis – The ARN of the data stream or a stream consumer.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – The ARN of the stream.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – The ARN of the queue.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – The ARN of the cluster.

    \n
  • \n
  • \n

    \n Amazon MQ – The ARN of the broker.

    \n
  • \n
  • \n

    \n Amazon DocumentDB – The ARN of the DocumentDB change stream.

    \n
  • \n
", "smithy.api#httpQuery": "EventSourceArn" } }, @@ -7640,7 +7990,7 @@ "CompatibleRuntime": { "target": "com.amazonaws.lambda#Runtime", "traits": { - "smithy.api#documentation": "

A runtime identifier. For example, go1.x.

", + "smithy.api#documentation": "

A runtime identifier. For example, go1.x.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

", "smithy.api#httpQuery": "CompatibleRuntime" } }, @@ -7738,7 +8088,7 @@ "CompatibleRuntime": { "target": "com.amazonaws.lambda#Runtime", "traits": { - "smithy.api#documentation": "

A runtime identifier. For example, go1.x.

", + "smithy.api#documentation": "

A runtime identifier. For example, go1.x.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

", "smithy.api#httpQuery": "CompatibleRuntime" } }, @@ -8525,7 +8875,7 @@ "CompatibleRuntimes": { "target": "com.amazonaws.lambda#CompatibleRuntimes", "traits": { - "smithy.api#documentation": "

A list of compatible function\n runtimes. Used for filtering with ListLayers and ListLayerVersions.

" + "smithy.api#documentation": "

A list of compatible function\n runtimes. Used for filtering with ListLayers and ListLayerVersions.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "LicenseInfo": { @@ -8588,7 +8938,7 @@ "CompatibleRuntimes": { "target": "com.amazonaws.lambda#CompatibleRuntimes", "traits": { - "smithy.api#documentation": "

The layer's compatible runtimes.

" + "smithy.api#documentation": "

The layer's compatible runtimes.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "LicenseInfo": { @@ -8888,7 +9238,7 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of an SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of an SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" + "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of a standard SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of a standard SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" } } }, @@ -9387,6 +9737,23 @@ "smithy.api#httpError": 502 } }, + "com.amazonaws.lambda#ResponseStreamingInvocationType": { + "type": "enum", + "members": { + "RequestResponse": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RequestResponse" + } + }, + "DryRun": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DryRun" + } + } + } + }, "com.amazonaws.lambda#RoleArn": { "type": "string", "traits": { @@ -9563,6 +9930,24 @@ "traits": { "smithy.api#enumValue": "nodejs18.x" } + }, + "python310": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "python3.10" + } + }, + "java17": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "java17" + } + }, + "ruby32": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ruby3.2" + } } } }, @@ -10698,7 +11083,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates an event source mapping. You can change the function that Lambda invokes, or pause\n invocation and resume later from the same location.

\n

For details about how to configure different event sources, see the following topics.

\n \n

The following error handling options are available only for stream sources (DynamoDB and Kinesis):

\n
    \n
  • \n

    \n BisectBatchOnFunctionError – If the function returns an error, split the batch in two and retry.

    \n
  • \n
  • \n

    \n DestinationConfig – Send discarded records to an Amazon SQS queue or Amazon SNS topic.

    \n
  • \n
  • \n

    \n MaximumRecordAgeInSeconds – Discard records older than the specified age. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires

    \n
  • \n
  • \n

    \n MaximumRetryAttempts – Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

    \n
  • \n
  • \n

    \n ParallelizationFactor – Process multiple batches from each shard concurrently.

    \n
  • \n
\n

For information about which configuration parameters apply to each event source, see the following topics.

\n ", + "smithy.api#documentation": "

Updates an event source mapping. You can change the function that Lambda invokes, or pause\n invocation and resume later from the same location.

\n

For details about how to configure different event sources, see the following topics.

\n \n

The following error handling options are available only for stream sources (DynamoDB and Kinesis):

\n
    \n
  • \n

    \n BisectBatchOnFunctionError – If the function returns an error, split the batch in two and retry.

    \n
  • \n
  • \n

    \n DestinationConfig – Send discarded records to an Amazon SQS queue or Amazon SNS topic.

    \n
  • \n
  • \n

    \n MaximumRecordAgeInSeconds – Discard records older than the specified age. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires

    \n
  • \n
  • \n

    \n MaximumRetryAttempts – Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

    \n
  • \n
  • \n

    \n ParallelizationFactor – Process multiple batches from each shard concurrently.

    \n
  • \n
\n

For information about which configuration parameters apply to each event source, see the following topics.

\n ", "smithy.api#http": { "method": "PUT", "uri": "/2015-03-31/event-source-mappings/{UUID}", @@ -10732,7 +11117,7 @@ "BatchSize": { "target": "com.amazonaws.lambda#BatchSize", "traits": { - "smithy.api#documentation": "

The maximum number of records in each batch that Lambda pulls from your stream or queue and sends to your function. Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation\n (6 MB).

\n
    \n
  • \n

    \n Amazon Kinesis – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Self-managed Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon MQ (ActiveMQ and RabbitMQ) – Default 100. Max 10,000.

    \n
  • \n
" + "smithy.api#documentation": "

The maximum number of records in each batch that Lambda pulls from your stream or queue and sends to your function. Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation\n (6 MB).

\n
    \n
  • \n

    \n Amazon Kinesis – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Self-managed Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon MQ (ActiveMQ and RabbitMQ) – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n DocumentDB – Default 100. Max 10,000.

    \n
  • \n
" } }, "FilterCriteria": { @@ -10744,37 +11129,37 @@ "MaximumBatchingWindowInSeconds": { "target": "com.amazonaws.lambda#MaximumBatchingWindowInSeconds", "traits": { - "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, and Amazon MQ event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" + "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, Amazon MQ, and DocumentDB event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" } }, "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

(Streams only) An Amazon SQS queue or Amazon SNS topic destination for discarded records.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) A standard Amazon SQS queue or standard Amazon SNS topic destination for discarded records.

" } }, "MaximumRecordAgeInSeconds": { "target": "com.amazonaws.lambda#MaximumRecordAgeInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records older than the specified age. The default value is infinite (-1).

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records older than the specified age. The default value is infinite (-1).

" } }, "BisectBatchOnFunctionError": { "target": "com.amazonaws.lambda#BisectBatchOnFunctionError", "traits": { - "smithy.api#documentation": "

(Streams only) If the function returns an error, split the batch in two and retry.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) If the function returns an error, split the batch in two and retry.

" } }, "MaximumRetryAttempts": { "target": "com.amazonaws.lambda#MaximumRetryAttemptsEventSourceMapping", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

" } }, "ParallelizationFactor": { "target": "com.amazonaws.lambda#ParallelizationFactor", "traits": { - "smithy.api#documentation": "

(Streams only) The number of batches to process from each shard concurrently.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The number of batches to process from each shard concurrently.

" } }, "SourceAccessConfigurations": { @@ -10786,13 +11171,13 @@ "TumblingWindowInSeconds": { "target": "com.amazonaws.lambda#TumblingWindowInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) The duration in seconds of a processing window. The range is between 1 second and 900 seconds.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The duration in seconds of a processing window for DynamoDB and Kinesis Streams event sources. A value of 0 seconds indicates no tumbling window.

" } }, "FunctionResponseTypes": { "target": "com.amazonaws.lambda#FunctionResponseTypeList", "traits": { - "smithy.api#documentation": "

(Streams and Amazon SQS) A list of current response type enums applied to the event source mapping.

" + "smithy.api#documentation": "

(Kinesis, DynamoDB Streams, and Amazon SQS) A list of current response type enums applied to the event source mapping.

" } }, "ScalingConfig": { @@ -11047,7 +11432,7 @@ "KMSKeyArn": { "target": "com.amazonaws.lambda#KMSKeyArn", "traits": { - "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) customer managed key that's used to encrypt your function's environment variables. When Lambda SnapStart is activated, this key is also used to encrypt your function's snapshot. If you don't provide a customer managed key, Lambda uses a default service key.

" + "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) customer managed key that's used to encrypt your function's \nenvironment variables. When \nLambda SnapStart is activated, Lambda also uses \nthis key is to encrypt your function's snapshot. If you deploy your function using a container image, Lambda also uses this key to \nencrypt your function when it's deployed. Note that this is not the same key that's used to protect your container image in the Amazon Elastic Container Registry (Amazon ECR).\nIf you don't provide a customer managed key, Lambda uses a default service key.

" } }, "TracingConfig": { @@ -11164,7 +11549,7 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of an SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of an SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" + "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of a standard SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of a standard SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" } } }, @@ -11235,6 +11620,12 @@ "traits": { "smithy.api#documentation": "

The cross-origin resource sharing (CORS) settings\n for your function URL.

" } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -11284,6 +11675,12 @@ "smithy.api#documentation": "

When the function URL configuration was last updated, in ISO-8601 format (YYYY-MM-DDThh:mm:ss.sTZD).

", "smithy.api#required": {} } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { diff --git a/aws/sdk/aws-models/polly.json b/aws/sdk/aws-models/polly.json index 70db8b9661..219c1a0a92 100644 --- a/aws/sdk/aws-models/polly.json +++ b/aws/sdk/aws-models/polly.json @@ -677,6 +677,12 @@ "traits": { "smithy.api#enumValue": "fi-FI" } + }, + "en_IE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "en-IE" + } } } }, @@ -1506,8 +1512,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1519,8 +1525,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1532,8 +1538,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1545,8 +1551,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1558,8 +1564,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1571,8 +1577,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1584,8 +1590,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1597,8 +1603,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1610,8 +1616,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1623,8 +1629,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1636,8 +1642,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1649,8 +1655,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1662,8 +1668,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1675,8 +1681,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1688,8 +1694,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1701,8 +1707,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1714,8 +1720,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1727,8 +1733,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1740,8 +1746,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1753,8 +1759,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1766,8 +1772,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1779,8 +1785,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1792,8 +1798,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1805,8 +1811,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1818,8 +1824,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1831,8 +1837,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1844,8 +1850,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1857,8 +1863,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1870,8 +1876,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1883,8 +1889,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1896,8 +1902,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1909,8 +1915,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1922,8 +1928,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1935,8 +1941,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1948,8 +1954,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1961,8 +1967,19 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1974,8 +1991,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1987,8 +2015,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2000,8 +2039,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2013,8 +2063,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2026,8 +2076,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2039,8 +2089,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2051,8 +2101,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2063,10 +2113,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -3321,6 +3377,18 @@ "traits": { "smithy.api#enumValue": "Tomoko" } + }, + "Niamh": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Niamh" + } + }, + "Sofie": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Sofie" + } } } }, diff --git a/aws/sdk/aws-models/qldb-session.json b/aws/sdk/aws-models/qldb-session.json index 37782b8b96..b12ca95320 100644 --- a/aws/sdk/aws-models/qldb-session.json +++ b/aws/sdk/aws-models/qldb-session.json @@ -772,8 +772,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -785,8 +785,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -798,8 +798,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -811,8 +811,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -824,8 +824,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -837,8 +837,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -850,8 +850,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -863,8 +863,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -876,8 +876,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -889,8 +889,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -902,8 +902,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -915,8 +915,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -928,8 +928,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -941,8 +941,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -954,8 +954,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -967,8 +967,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -980,8 +980,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -993,8 +993,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1006,8 +1006,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1019,8 +1019,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1032,8 +1032,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1045,8 +1045,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1058,8 +1058,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1071,8 +1071,19 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1084,8 +1095,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1097,8 +1119,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1110,8 +1143,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1123,8 +1167,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1136,8 +1180,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1149,8 +1193,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1161,8 +1205,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1173,10 +1217,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/route53.json b/aws/sdk/aws-models/route53.json index 8c5011b2f3..72d97074e3 100644 --- a/aws/sdk/aws-models/route53.json +++ b/aws/sdk/aws-models/route53.json @@ -414,216 +414,91 @@ }, "aws" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] + "ref": "UseFIPS" }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://route53.amazonaws.com", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-east-1" } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "aws" ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } + true ] }, { - "conditions": [], - "endpoint": { - "url": "https://route53.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://route53-fips.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -641,208 +516,40 @@ }, "aws-cn" ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" - } - ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.amazonaws.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://route53.amazonaws.com.cn", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" + "name": "sigv4", + "signingName": "route53", + "signingRegion": "cn-northwest-1" } ] }, - { - "conditions": [], - "endpoint": { - "url": "https://route53.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "cn-northwest-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -860,216 +567,91 @@ }, "aws-us-gov" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] + "ref": "UseFIPS" }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://route53.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-gov-west-1" } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "aws-us-gov" ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } + true ] }, { - "conditions": [], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-gov-west-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://route53.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-gov-west-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1087,80 +669,40 @@ }, "aws-iso" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.c2s.ic.gov", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [], - "endpoint": { - "url": "https://route53.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-iso-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://route53.c2s.ic.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-iso-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1178,80 +720,40 @@ }, "aws-iso-b" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.sc2s.sgov.gov", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [], - "endpoint": { - "url": "https://route53.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-isob-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://route53.sc2s.sgov.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-isob-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1373,60 +875,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://route53-fips.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-us-gov-global" - ] - } - ], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1509,141 +957,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://route53.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-cn-global" - ] - } - ], - "endpoint": { - "url": "https://route53.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "cn-northwest-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-us-gov-global" - ] - } - ], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-global" - ] - } - ], - "endpoint": { - "url": "https://route53.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-iso-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-b-global" - ] - } - ], - "endpoint": { - "url": "https://route53.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-isob-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1688,8 +1001,8 @@ }, "params": { "Region": "aws-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1710,8 +1023,8 @@ }, "params": { "Region": "aws-global", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1723,8 +1036,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1745,8 +1058,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1758,8 +1071,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1780,8 +1093,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1802,8 +1115,8 @@ }, "params": { "Region": "aws-cn-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1815,8 +1128,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1828,8 +1141,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1841,8 +1154,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1863,8 +1176,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1885,8 +1198,8 @@ }, "params": { "Region": "aws-us-gov-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1907,8 +1220,8 @@ }, "params": { "Region": "aws-us-gov-global", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1920,8 +1233,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1942,8 +1255,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1955,8 +1268,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1977,8 +1290,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1999,8 +1312,19 @@ }, "params": { "Region": "aws-iso-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2012,8 +1336,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2034,8 +1369,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2056,8 +1391,19 @@ }, "params": { "Region": "aws-iso-b-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2069,8 +1415,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2091,8 +1448,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2104,8 +1461,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2117,8 +1474,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2129,8 +1486,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2141,10 +1498,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/s3.json b/aws/sdk/aws-models/s3.json index 57c5645bde..f0227a4af8 100644 --- a/aws/sdk/aws-models/s3.json +++ b/aws/sdk/aws-models/s3.json @@ -44,7 +44,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies the days since the initiation of an incomplete multipart upload that Amazon S3 will\n wait before permanently removing all parts of the upload. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies the days since the initiation of an incomplete multipart upload that Amazon S3 will\n wait before permanently removing all parts of the upload. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration in the\n Amazon S3 User Guide.

" } }, "com.amazonaws.s3#AbortMultipartUpload": { @@ -61,7 +61,19 @@ } ], "traits": { - "smithy.api#documentation": "

This action aborts a multipart upload. After a multipart upload is aborted, no\n additional parts can be uploaded using that upload ID. The storage consumed by any\n previously uploaded parts will be freed. However, if any part uploads are currently in\n progress, those part uploads might or might not succeed. As a result, it might be necessary\n to abort a given multipart upload multiple times in order to completely free all storage\n consumed by all parts.

\n

To verify that all parts have been removed, so you don't get charged for the part\n storage, you should call the ListParts action and ensure that\n the parts list is empty.

\n

For information about permissions required to use the multipart upload, see Multipart Upload and\n Permissions.

\n

The following operations are related to AbortMultipartUpload:

\n ", + "smithy.api#documentation": "

This action aborts a multipart upload. After a multipart upload is aborted, no\n additional parts can be uploaded using that upload ID. The storage consumed by any\n previously uploaded parts will be freed. However, if any part uploads are currently in\n progress, those part uploads might or might not succeed. As a result, it might be necessary\n to abort a given multipart upload multiple times in order to completely free all storage\n consumed by all parts.

\n

To verify that all parts have been removed, so you don't get charged for the part\n storage, you should call the ListParts action and ensure that\n the parts list is empty.

\n

For information about permissions required to use the multipart upload, see Multipart Upload\n and Permissions.

\n

The following operations are related to AbortMultipartUpload:

\n ", + "smithy.api#examples": [ + { + "title": "To abort a multipart upload", + "documentation": "The following example aborts a multipart upload.", + "input": { + "Bucket": "examplebucket", + "Key": "bigobject", + "UploadId": "xadcOB_7YPBOJuoFiQ9cz4P3Pe6FIZwO4f7wN93uHsNBEw97pl5eNwzExg0LAT2dUN91cOmrEQHDsP3WA60CEg--" + }, + "output": {} + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?x-id=AbortMultipartUpload", @@ -89,7 +101,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name to which the upload was taking place.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name to which the upload was taking place.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -15276,6 +15288,1366 @@ "Accelerate": false } }, + { + "documentation": "non-bucket endpoint with FIPS: TODO(descriptive)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://beta.example.com:1234/path" + } + }, + "params": { + "Region": "us-west-2", + "Endpoint": "http://beta.example.com:1234/path", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "FIPS + dualstack + custom endpoint TODO(descriptive)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://beta.example.com:1234/path" + } + }, + "params": { + "Region": "us-west-2", + "Endpoint": "http://beta.example.com:1234/path", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "dualstack + custom endpoint TODO(descriptive)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://beta.example.com:1234/path" + } + }, + "params": { + "Region": "us-west-2", + "Endpoint": "http://beta.example.com:1234/path", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "custom endpoint without FIPS/dualstack", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://beta.example.com:1234/path" + } + }, + "params": { + "Region": "us-west-2", + "Endpoint": "http://beta.example.com:1234/path", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "s3 object lambda with access points disabled", + "expect": { + "error": "Access points are not supported for this operation" + }, + "params": { + "Region": "us-west-2", + "Bucket": "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:myendpoint", + "DisableAccessPoints": true + } + }, + { + "documentation": "non bucket + FIPS", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3-fips.us-west-2.amazonaws.com" + } + }, + "params": { + "Region": "us-west-2", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "standard non bucket endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.us-west-2.amazonaws.com" + } + }, + "params": { + "Region": "us-west-2", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "non bucket endpoint with FIPS + Dualstack", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3-fips.dualstack.us-west-2.amazonaws.com" + } + }, + "params": { + "Region": "us-west-2", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "non bucket endpoint with dualstack", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.dualstack.us-west-2.amazonaws.com" + } + }, + "params": { + "Region": "us-west-2", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "use global endpoint + IP address endpoint override", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://127.0.0.1/bucket" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket", + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "http://127.0.0.1", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "non-dns endpoint + global endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": false, + "UseDualStack": false, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "endpoint override + use global endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": false, + "UseDualStack": false, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "FIPS + dualstack + non-bucket endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "FIPS + dualstack + non-DNS endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "endpoint override + FIPS + dualstack (BUG)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseFIPS": true, + "UseDualStack": false, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "endpoint override + non-dns bucket + FIPS (BUG)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": true, + "UseDualStack": false, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "FIPS + bucket endpoint + force path style", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseFIPS": true, + "UseDualStack": false, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "bucket + FIPS + force path style", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket", + "ForcePathStyle": true, + "UseFIPS": true, + "UseDualStack": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "FIPS + dualstack + use global endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://bucket.s3-fips.dualstack.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket", + "UseFIPS": true, + "UseDualStack": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "URI encoded bucket + use global endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": true, + "UseDualStack": false, + "UseGlobalEndpoint": true, + "Endpoint": "https://foo.com" + } + }, + { + "documentation": "FIPS + path based endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": true, + "UseDualStack": false, + "Accelerate": false, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "accelerate + dualstack + global endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://bucket.s3-accelerate.dualstack.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket", + "UseFIPS": false, + "UseDualStack": true, + "Accelerate": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "dualstack + global endpoint + non URI safe bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "Accelerate": false, + "UseDualStack": true, + "UseFIPS": false, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "FIPS + uri encoded bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "Accelerate": false, + "UseDualStack": false, + "UseFIPS": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "endpoint override + non-uri safe endpoint + force path style", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "Accelerate": false, + "UseDualStack": false, + "UseFIPS": true, + "Endpoint": "http://foo.com", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "FIPS + Dualstack + global endpoint + non-dns bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "Accelerate": false, + "UseDualStack": true, + "UseFIPS": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "endpoint override + FIPS + dualstack (this is wrong—it's a bug in the UseGlobalEndpoint branch)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": true, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "non-bucket endpoint override + dualstack + global endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "Endpoint override + UseGlobalEndpoint + us-east-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "non-FIPS partition with FIPS set + custom endpoint", + "expect": { + "error": "Partition does not support FIPS" + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": false, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "aws-global signs as us-east-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseFIPS": true, + "Accelerate": false, + "UseDualStack": true + } + }, + { + "documentation": "aws-global signs as us-east-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket.foo.com" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket", + "UseDualStack": false, + "UseFIPS": false, + "Accelerate": false, + "Endpoint": "https://foo.com" + } + }, + { + "documentation": "aws-global + dualstack + path-only bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseDualStack": true, + "UseFIPS": false, + "Accelerate": false + } + }, + { + "documentation": "aws-global + path-only bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!" + } + }, + { + "documentation": "aws-global + fips + custom endpoint (TODO: should be an error)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseDualStack": false, + "UseFIPS": true, + "Accelerate": false, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "aws-global, endpoint override & path only-bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseDualStack": false, + "UseFIPS": false, + "Accelerate": false, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "aws-global + dualstack + custom endpoint (TODO: should be an error)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "aws-global", + "UseDualStack": true, + "UseFIPS": false, + "Accelerate": false, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "accelerate, dualstack + aws-global", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket.s3-accelerate.dualstack.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket", + "UseDualStack": true, + "UseFIPS": false, + "Accelerate": true + } + }, + { + "documentation": "FIPS + aws-global + path only bucket. TODO: this should be an error", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseDualStack": true, + "UseFIPS": true, + "Accelerate": false + } + }, + { + "documentation": "aws-global + FIPS + endpoint override. TODO: should this be an error?", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "aws-global", + "UseFIPS": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "force path style, aws-global & endpoint override", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseFIPS": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "ip address causes path style to be forced", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://192.168.1.1/bucket" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket", + "Endpoint": "http://192.168.1.1" + } + }, + { + "documentation": "endpoint override with aws-global region", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "aws-global", + "UseFIPS": true, + "UseDualStack": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "FIPS + path-only (TODO: consider making this an error)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3-fips.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseFIPS": true + } + }, + { + "documentation": "empty arn type", + "expect": { + "error": "Invalid ARN: No ARN type specified" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:not-s3:us-west-2:123456789012::myendpoint" + } + }, + { + "documentation": "path style can't be used with accelerate", + "expect": { + "error": "Path-style addressing cannot be used with S3 Accelerate" + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket!", + "Accelerate": true + } + }, + { + "documentation": "invalid region", + "expect": { + "error": "Invalid region: region was not a valid DNS name." + }, + "params": { + "Region": "us-east-2!", + "Bucket": "bucket.subdomain", + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "invalid region", + "expect": { + "error": "Invalid region: region was not a valid DNS name." + }, + "params": { + "Region": "us-east-2!", + "Bucket": "bucket", + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "empty arn type", + "expect": { + "error": "Invalid Access Point Name" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3::123456789012:accesspoint:my_endpoint" + } + }, + { + "documentation": "empty arn type", + "expect": { + "error": "Client was configured for partition `aws` but ARN (`arn:aws:s3:cn-north-1:123456789012:accesspoint:my-endpoint`) has `aws-cn`" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3:cn-north-1:123456789012:accesspoint:my-endpoint", + "UseArnRegion": true + } + }, + { + "documentation": "invalid arn region", + "expect": { + "error": "Invalid region in ARN: `us-east_2` (invalid DNS name)" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-object-lambda:us-east_2:123456789012:accesspoint:my-endpoint", + "UseArnRegion": true + } + }, + { + "documentation": "invalid ARN outpost", + "expect": { + "error": "Invalid ARN: The outpost Id may only contain a-z, A-Z, 0-9 and `-`. Found: `op_01234567890123456`" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:123456789012:outpost/op_01234567890123456/accesspoint/reports", + "UseArnRegion": true + } + }, + { + "documentation": "invalid ARN", + "expect": { + "error": "Invalid ARN: expected an access point name" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:123456789012:outpost/op-01234567890123456/reports" + } + }, + { + "documentation": "invalid ARN", + "expect": { + "error": "Invalid ARN: Expected a 4-component resource" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:123456789012:outpost/op-01234567890123456" + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "error": "Expected an outpost type `accesspoint`, found not-accesspoint" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:123456789012:outpost/op-01234567890123456/not-accesspoint/reports" + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "error": "Invalid region in ARN: `us-east_1` (invalid DNS name)" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east_1:123456789012:outpost/op-01234567890123456/not-accesspoint/reports" + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "error": "Invalid ARN: The account id may only contain a-z, A-Z, 0-9 and `-`. Found: `12345_789012`" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:12345_789012:outpost/op-01234567890123456/not-accesspoint/reports" + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "error": "Invalid ARN: The Outpost Id was not set" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:12345789012:outpost" + } + }, + { + "documentation": "use global endpoint virtual addressing", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://bucket.example.com" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket", + "Endpoint": "http://example.com", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "global endpoint + ip address", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://192.168.0.1/bucket" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket", + "Endpoint": "http://192.168.0.1", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.us-east-2.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket!", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket.s3-accelerate.amazonaws.com" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket", + "Accelerate": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "use global endpoint + custom endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket!", + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "use global endpoint, not us-east-1, force path style", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket!", + "UseGlobalEndpoint": true, + "ForcePathStyle": true, + "Endpoint": "http://foo.com" + } + }, { "documentation": "vanilla virtual addressing@us-west-2", "expect": { @@ -19143,6 +20515,122 @@ "UseDualStack": false, "Accelerate": false } + }, + { + "documentation": "S3 Outposts Abba - No endpoint set for beta", + "expect": { + "error": "Expected a endpoint to be specified but no endpoint was found" + }, + "params": { + "Region": "us-east-1", + "Bucket": "test-accessp-e0b1d075431d83bebde8xz5w8ijx1qzlbp3i3ebeta0--op-s3", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow with bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://10.0.1.12:433/bucketName" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "http://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow without bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://10.0.1.12:433" + } + }, + "params": { + "Region": "snow", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow no port", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://10.0.1.12/bucketName" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "http://10.0.1.12", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow dns endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://amazonaws.com/bucketName" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://amazonaws.com", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } } ], "version": "1.0" @@ -19196,7 +20684,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies the configuration and any analyses for the analytics filter of an Amazon S3 bucket.

" + "smithy.api#documentation": "

Specifies the configuration and any analyses for the analytics filter of an Amazon S3\n bucket.

" } }, "com.amazonaws.s3#AnalyticsConfigurationList": { @@ -19326,7 +20814,7 @@ "CreationDate": { "target": "com.amazonaws.s3#CreationDate", "traits": { - "smithy.api#documentation": "

Date the bucket was created. This date can change when making changes to your bucket, such as editing its bucket policy.

" + "smithy.api#documentation": "

Date the bucket was created. This date can change when making changes to your bucket,\n such as editing its bucket policy.

" } } }, @@ -19681,7 +21169,7 @@ } }, "traits": { - "smithy.api#documentation": "

Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more\n information, see Enabling\n Cross-Origin Resource Sharing in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more\n information, see Enabling\n Cross-Origin Resource Sharing in the\n Amazon S3 User Guide.

" } }, "com.amazonaws.s3#CORSRule": { @@ -19757,7 +21245,7 @@ "Comments": { "target": "com.amazonaws.s3#Comments", "traits": { - "smithy.api#documentation": "

A single character used to indicate that a row should be ignored when the character is\n present at the start of that row. You can specify any character to indicate a comment\n line.

" + "smithy.api#documentation": "

A single character used to indicate that a row should be ignored when the character is\n present at the start of that row. You can specify any character to indicate a comment line.\n The default character is #.

\n

Default: #\n

" } }, "QuoteEscapeCharacter": { @@ -19962,7 +21450,7 @@ "target": "com.amazonaws.s3#CompleteMultipartUploadOutput" }, "traits": { - "smithy.api#documentation": "

Completes a multipart upload by assembling previously uploaded parts.

\n

You first initiate the multipart upload and then upload all parts using the UploadPart\n operation. After successfully uploading all relevant parts of an upload, you call this\n action to complete the upload. Upon receiving this request, Amazon S3 concatenates all\n the parts in ascending order by part number to create a new object. In the Complete\n Multipart Upload request, you must provide the parts list. You must ensure that the parts\n list is complete. This action concatenates the parts that you provide in the list. For\n each part in the list, you must provide the part number and the ETag value,\n returned after that part was uploaded.

\n

Processing of a Complete Multipart Upload request could take several minutes to\n complete. After Amazon S3 begins processing the request, it sends an HTTP response header that\n specifies a 200 OK response. While processing is in progress, Amazon S3 periodically sends white\n space characters to keep the connection from timing out. Because a request could fail after\n the initial 200 OK response has been sent, it is important that you check the response body\n to determine whether the request succeeded.

\n

Note that if CompleteMultipartUpload fails, applications should be prepared\n to retry the failed requests. For more information, see Amazon S3 Error Best Practices.

\n \n

You cannot use Content-Type: application/x-www-form-urlencoded with Complete\n Multipart Upload requests. Also, if you do not provide a Content-Type header, CompleteMultipartUpload returns a 200 OK response.

\n
\n

For more information about multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information about permissions required to use the multipart upload API, see Multipart Upload and\n Permissions.

\n

\n CompleteMultipartUpload has the following special errors:

\n
    \n
  • \n

    Error code: EntityTooSmall\n

    \n
      \n
    • \n

      Description: Your proposed upload is smaller than the minimum allowed object\n size. Each part must be at least 5 MB in size, except the last part.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InvalidPart\n

    \n
      \n
    • \n

      Description: One or more of the specified parts could not be found. The part\n might not have been uploaded, or the specified entity tag might not have\n matched the part's entity tag.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InvalidPartOrder\n

    \n
      \n
    • \n

      Description: The list of parts was not in ascending order. The parts list\n must be specified in order by part number.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: NoSuchUpload\n

    \n
      \n
    • \n

      Description: The specified multipart upload does not exist. The upload ID\n might be invalid, or the multipart upload might have been aborted or\n completed.

      \n
    • \n
    • \n

      404 Not Found

      \n
    • \n
    \n
  • \n
\n

The following operations are related to CompleteMultipartUpload:

\n ", + "smithy.api#documentation": "

Completes a multipart upload by assembling previously uploaded parts.

\n

You first initiate the multipart upload and then upload all parts using the UploadPart\n operation. After successfully uploading all relevant parts of an upload, you call this\n action to complete the upload. Upon receiving this request, Amazon S3 concatenates all the\n parts in ascending order by part number to create a new object. In the Complete Multipart\n Upload request, you must provide the parts list. You must ensure that the parts list is\n complete. This action concatenates the parts that you provide in the list. For each part in\n the list, you must provide the part number and the ETag value, returned after\n that part was uploaded.

\n

Processing of a Complete Multipart Upload request could take several minutes to\n complete. After Amazon S3 begins processing the request, it sends an HTTP response header that\n specifies a 200 OK response. While processing is in progress, Amazon S3 periodically sends white\n space characters to keep the connection from timing out. A request could fail after the\n initial 200 OK response has been sent. This means that a 200 OK response can\n contain either a success or an error. If you call the S3 API directly, make sure to design\n your application to parse the contents of the response and handle it appropriately. If you\n use Amazon Web Services SDKs, SDKs handle this condition. The SDKs detect the embedded error and apply\n error handling per your configuration settings (including automatically retrying the\n request as appropriate). If the condition persists, the SDKs throws an exception (or, for\n the SDKs that don't use exceptions, they return the error).

\n

Note that if CompleteMultipartUpload fails, applications should be prepared\n to retry the failed requests. For more information, see Amazon S3 Error Best\n Practices.

\n \n

You cannot use Content-Type: application/x-www-form-urlencoded with\n Complete Multipart Upload requests. Also, if you do not provide a\n Content-Type header, CompleteMultipartUpload returns a 200\n OK response.

\n
\n

For more information about multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information about permissions required to use the multipart upload API, see Multipart Upload\n and Permissions.

\n

\n CompleteMultipartUpload has the following special errors:

\n
    \n
  • \n

    Error code: EntityTooSmall\n

    \n
      \n
    • \n

      Description: Your proposed upload is smaller than the minimum allowed object\n size. Each part must be at least 5 MB in size, except the last part.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InvalidPart\n

    \n
      \n
    • \n

      Description: One or more of the specified parts could not be found. The part\n might not have been uploaded, or the specified entity tag might not have\n matched the part's entity tag.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InvalidPartOrder\n

    \n
      \n
    • \n

      Description: The list of parts was not in ascending order. The parts list\n must be specified in order by part number.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: NoSuchUpload\n

    \n
      \n
    • \n

      Description: The specified multipart upload does not exist. The upload ID\n might be invalid, or the multipart upload might have been aborted or\n completed.

      \n
    • \n
    • \n

      404 Not Found

      \n
    • \n
    \n
  • \n
\n

The following operations are related to CompleteMultipartUpload:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?x-id=CompleteMultipartUpload", @@ -19982,7 +21470,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket that contains the newly created object. Does not return the access point ARN or access point alias if used.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

The name of the bucket that contains the newly created object. Does not return the access point\n ARN or access point alias if used.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

" } }, "Key": { @@ -20001,7 +21489,7 @@ "ETag": { "target": "com.amazonaws.s3#ETag", "traits": { - "smithy.api#documentation": "

Entity tag that identifies the newly created object's data. Objects with different\n object data will have different entity tags. The entity tag is an opaque string. The entity\n tag may or may not be an MD5 digest of the object data. If the entity tag is not an MD5\n digest of the object data, it will contain one or more nonhexadecimal characters and/or\n will consist of less than 32 or more than 32 hexadecimal digits. For more information about\n how the entity tag is calculated, see\n Checking\n object integrity in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Entity tag that identifies the newly created object's data. Objects with different\n object data will have different entity tags. The entity tag is an opaque string. The entity\n tag may or may not be an MD5 digest of the object data. If the entity tag is not an MD5\n digest of the object data, it will contain one or more nonhexadecimal characters and/or\n will consist of less than 32 or more than 32 hexadecimal digits. For more information about\n how the entity tag is calculated, see Checking object\n integrity in the Amazon S3 User Guide.

" } }, "ChecksumCRC32": { @@ -20031,7 +21519,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

If you specified server-side encryption either with an Amazon S3-managed encryption key or an\n Amazon Web Services KMS key in your initiate multipart upload request, the response\n includes this header. It confirms the encryption algorithm that Amazon S3 used to encrypt the\n object.

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -20045,7 +21533,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -20053,7 +21541,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20075,7 +21563,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -20180,7 +21668,7 @@ "Parts": { "target": "com.amazonaws.s3#CompletedPartList", "traits": { - "smithy.api#documentation": "

Array of CompletedPart data types.

\n

If you do not supply a valid Part with your request, the service sends back an HTTP\n 400 response.

", + "smithy.api#documentation": "

Array of CompletedPart data types.

\n

If you do not supply a valid Part with your request, the service sends back\n an HTTP 400 response.

", "smithy.api#xmlFlattened": {}, "smithy.api#xmlName": "Part" } @@ -20335,7 +21823,24 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a copy of an object that is already stored in Amazon S3.

\n \n

You can store individual objects of up to 5 TB in Amazon S3. You create a copy of your\n object up to 5 GB in size in a single atomic action using this API. However, to copy an\n object greater than 5 GB, you must use the multipart upload Upload Part - Copy\n (UploadPartCopy) API. For more information, see Copy Object Using the\n REST Multipart Upload API.

\n
\n

All copy requests must be authenticated. Additionally, you must have\n read access to the source object and write\n access to the destination bucket. For more information, see REST Authentication. Both the Region\n that you want to copy the object from and the Region that you want to copy the object to\n must be enabled for your account.

\n

A copy request might return an error when Amazon S3 receives the copy request or while Amazon S3\n is copying the files. If the error occurs before the copy action starts, you receive a\n standard Amazon S3 error. If the error occurs during the copy operation, the error response is\n embedded in the 200 OK response. This means that a 200 OK\n response can contain either a success or an error. Design your application to parse the\n contents of the response and handle it appropriately.

\n

If the copy is successful, you receive a response with information about the copied\n object.

\n \n

If the request is an HTTP 1.1 request, the response is chunk encoded. If it were not,\n it would not contain the content-length, and you would need to read the entire\n body.

\n
\n

The copy request charge is based on the storage class and Region that you specify for\n the destination object. For pricing information, see Amazon S3 pricing.

\n \n

Amazon S3 transfer acceleration does not support cross-Region copies. If you request a\n cross-Region copy using a transfer acceleration endpoint, you get a 400 Bad\n Request error. For more information, see Transfer Acceleration.

\n
\n

\n Metadata\n

\n

When copying an object, you can preserve all metadata (default) or specify new metadata.\n However, the ACL is not preserved and is set to private for the user making the request. To\n override the default ACL setting, specify a new ACL when generating a copy request. For\n more information, see Using ACLs.

\n

To specify whether you want the object metadata copied from the source object or\n replaced with metadata provided in the request, you can optionally add the\n x-amz-metadata-directive header. When you grant permissions, you can use\n the s3:x-amz-metadata-directive condition key to enforce certain metadata\n behavior when objects are uploaded. For more information, see Specifying Conditions in a\n Policy in the Amazon S3 User Guide. For a complete list of\n Amazon S3-specific condition keys, see Actions, Resources, and Condition Keys for\n Amazon S3.

\n

\n x-amz-copy-source-if Headers\n

\n

To only copy an object under certain conditions, such as whether the Etag\n matches or whether the object was modified before or after a specified date, use the\n following request parameters:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-none-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since\n

    \n
  • \n
\n

If both the x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the request\n and evaluate as follows, Amazon S3 returns 200 OK and copies the data:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match condition evaluates to true

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false

    \n
  • \n
\n

If both the x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the request and\n evaluate as follows, Amazon S3 returns the 412 Precondition Failed response\n code:

\n
    \n
  • \n

    \n x-amz-copy-source-if-none-match condition evaluates to false

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true

    \n
  • \n
\n \n

All headers with the x-amz- prefix, including\n x-amz-copy-source, must be signed.

\n
\n

\n Server-side encryption\n

\n

When you perform a CopyObject operation, you can optionally use the appropriate encryption-related \n headers to encrypt the object using server-side encryption with Amazon Web Services managed encryption keys \n (SSE-S3 or SSE-KMS) or a customer-provided encryption key. With server-side encryption, Amazon S3 \n encrypts your data as it writes it to disks in its data centers and decrypts the data when \n you access it. For more information about server-side encryption, see Using\n Server-Side Encryption.

\n

If a target object uses SSE-KMS, you can enable an S3 Bucket Key for the object. For more\n information, see Amazon S3 Bucket Keys in the Amazon S3 User Guide.

\n

\n Access Control List (ACL)-Specific Request\n Headers\n

\n

When copying an object, you can optionally use headers to grant ACL-based permissions.\n By default, all objects are private. Only the owner has full access control. When adding a\n new object, you can grant permissions to individual Amazon Web Services accounts or to predefined groups\n defined by Amazon S3. These permissions are then added to the ACL on the object. For more\n information, see Access Control List (ACL) Overview and Managing ACLs Using the REST\n API.

\n

If the bucket that you're copying objects to uses the bucket owner enforced setting for\n S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that\n use this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control canned\n ACL or an equivalent form of this ACL expressed in the XML format.

\n

For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, \n all objects written to the bucket by any account will be owned by the bucket owner.

\n
\n

\n Checksums\n

\n

When copying an object, if it has a checksum, that checksum will be copied to the new object\n by default. When you copy the object over, you may optionally specify a different checksum\n algorithm to use with the x-amz-checksum-algorithm header.

\n

\n Storage Class Options\n

\n

You can use the CopyObject action to change the storage class of an\n object that is already stored in Amazon S3 using the StorageClass parameter. For\n more information, see Storage\n Classes in the Amazon S3 User Guide.

\n

\n Versioning\n

\n

By default, x-amz-copy-source identifies the current version of an object\n to copy. If the current version is a delete marker, Amazon S3 behaves as if the object was\n deleted. To copy a different version, use the versionId subresource.

\n

If you enable versioning on the target bucket, Amazon S3 generates a unique version ID for\n the object being copied. This version ID is different from the version ID of the source\n object. Amazon S3 returns the version ID of the copied object in the\n x-amz-version-id response header in the response.

\n

If you do not enable versioning or suspend it on the target bucket, the version ID that\n Amazon S3 generates is always null.

\n

If the source object's storage class is GLACIER, you must restore a copy of this object\n before you can use it as a source object for the copy operation. For more information, see\n RestoreObject.

\n

The following operations are related to CopyObject:

\n \n

For more information, see Copying\n Objects.

", + "smithy.api#documentation": "

Creates a copy of an object that is already stored in Amazon S3.

\n \n

You can store individual objects of up to 5 TB in Amazon S3. You create a copy of your\n object up to 5 GB in size in a single atomic action using this API. However, to copy an\n object greater than 5 GB, you must use the multipart upload Upload Part - Copy\n (UploadPartCopy) API. For more information, see Copy Object Using the\n REST Multipart Upload API.

\n
\n

All copy requests must be authenticated. Additionally, you must have\n read access to the source object and write\n access to the destination bucket. For more information, see REST Authentication. Both the\n Region that you want to copy the object from and the Region that you want to copy the\n object to must be enabled for your account.

\n

A copy request might return an error when Amazon S3 receives the copy request or while Amazon S3\n is copying the files. If the error occurs before the copy action starts, you receive a\n standard Amazon S3 error. If the error occurs during the copy operation, the error response is\n embedded in the 200 OK response. This means that a 200 OK\n response can contain either a success or an error. If you call the S3 API directly, make\n sure to design your application to parse the contents of the response and handle it\n appropriately. If you use Amazon Web Services SDKs, SDKs handle this condition. The SDKs detect the\n embedded error and apply error handling per your configuration settings (including\n automatically retrying the request as appropriate). If the condition persists, the SDKs\n throws an exception (or, for the SDKs that don't use exceptions, they return the\n error).

\n

If the copy is successful, you receive a response with information about the copied\n object.

\n \n

If the request is an HTTP 1.1 request, the response is chunk encoded. If it were not,\n it would not contain the content-length, and you would need to read the entire\n body.

\n
\n

The copy request charge is based on the storage class and Region that you specify for\n the destination object. For pricing information, see Amazon S3 pricing.

\n \n

Amazon S3 transfer acceleration does not support cross-Region copies. If you request a\n cross-Region copy using a transfer acceleration endpoint, you get a 400 Bad\n Request error. For more information, see Transfer\n Acceleration.

\n
\n
\n
Metadata
\n
\n

When copying an object, you can preserve all metadata (the default) or specify new metadata.\n However, the access control list (ACL) is not preserved and is set to private for the user making the request. To\n override the default ACL setting, specify a new ACL when generating a copy request. For\n more information, see Using ACLs.

\n

To specify whether you want the object metadata copied from the source object or\n replaced with metadata provided in the request, you can optionally add the\n x-amz-metadata-directive header. When you grant permissions, you can use\n the s3:x-amz-metadata-directive condition key to enforce certain metadata\n behavior when objects are uploaded. For more information, see Specifying Conditions in a\n Policy in the Amazon S3 User Guide. For a complete list of\n Amazon S3-specific condition keys, see Actions, Resources, and Condition Keys for\n Amazon S3.

\n \n

\n x-amz-website-redirect-location is unique to each object and must be\n specified in the request headers to copy the value.

\n
\n
\n
x-amz-copy-source-if Headers
\n
\n

To only copy an object under certain conditions, such as whether the Etag\n matches or whether the object was modified before or after a specified date, use the\n following request parameters:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-none-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since\n

    \n
  • \n
\n

If both the x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the request\n and evaluate as follows, Amazon S3 returns 200 OK and copies the data:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match condition evaluates to true

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false

    \n
  • \n
\n

If both the x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the request and\n evaluate as follows, Amazon S3 returns the 412 Precondition Failed response\n code:

\n
    \n
  • \n

    \n x-amz-copy-source-if-none-match condition evaluates to false

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true

    \n
  • \n
\n \n

All headers with the x-amz- prefix, including\n x-amz-copy-source, must be signed.

\n
\n
\n
Server-side encryption
\n
\n

Amazon S3 automatically encrypts all new objects that are copied to an S3 bucket. When\n copying an object, if you don't specify encryption information in your copy\n request, the encryption setting of the target object is set to the default\n encryption configuration of the destination bucket. By default, all buckets have a\n base level of encryption configuration that uses server-side encryption with Amazon S3\n managed keys (SSE-S3). If the destination bucket has a default encryption\n configuration that uses server-side encryption with Key Management Service (KMS) keys\n (SSE-KMS), dual-layer server-side encryption with Amazon Web Services KMS keys (DSSE-KMS), or\n server-side encryption with customer-provided encryption keys (SSE-C), Amazon S3 uses\n the corresponding KMS key, or a customer-provided key to encrypt the target\n object copy.

\n

When you perform a CopyObject operation, if you want to use a different type\n of encryption setting for the target object, you can use other appropriate\n encryption-related headers to encrypt the target object with a KMS key, an Amazon S3 managed\n key, or a customer-provided key. With server-side encryption, Amazon S3 encrypts your data as it\n writes your data to disks in its data centers and decrypts the data when you access it. If the\n encryption setting in your request is different from the default encryption configuration\n of the destination bucket, the encryption setting in your request takes precedence. If the\n source object for the copy is stored in Amazon S3 using SSE-C, you must provide the necessary\n encryption information in your request so that Amazon S3 can decrypt the object for copying. For\n more information about server-side encryption, see Using Server-Side\n Encryption.

\n

If a target object uses SSE-KMS, you can enable an S3 Bucket Key for the\n object. For more information, see Amazon S3 Bucket Keys in the\n Amazon S3 User Guide.

\n
\n
Access Control List (ACL)-Specific Request\n Headers
\n
\n

When copying an object, you can optionally use headers to grant ACL-based permissions.\n By default, all objects are private. Only the owner has full access control. When adding a\n new object, you can grant permissions to individual Amazon Web Services accounts or to predefined groups\n that are defined by Amazon S3. These permissions are then added to the ACL on the object. For more\n information, see Access Control List (ACL) Overview and Managing ACLs Using the REST\n API.

\n

If the bucket that you're copying objects to uses the bucket owner enforced setting for\n S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that use\n this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control\n canned ACL or an equivalent form of this ACL expressed in the XML format.

\n

For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, all\n objects written to the bucket by any account will be owned by the bucket owner.

\n
\n
\n
Checksums
\n
\n

When copying an object, if it has a checksum, that checksum will be copied to the new\n object by default. When you copy the object over, you can optionally specify a different\n checksum algorithm to use with the x-amz-checksum-algorithm header.

\n
\n
Storage Class Options
\n
\n

You can use the CopyObject action to change the storage class of an object\n that is already stored in Amazon S3 by using the StorageClass parameter. For more\n information, see Storage Classes in the\n Amazon S3 User Guide.

\n

If the source object's storage class is GLACIER, you must restore a copy of\n this object before you can use it as a source object for the copy operation. For\n more information, see RestoreObject. For\n more information, see Copying\n Objects.

\n
\n
Versioning
\n
\n

By default, x-amz-copy-source header identifies the current version of an object\n to copy. If the current version is a delete marker, Amazon S3 behaves as if the object was\n deleted. To copy a different version, use the versionId subresource.

\n

If you enable versioning on the target bucket, Amazon S3 generates a unique version ID for\n the object being copied. This version ID is different from the version ID of the source\n object. Amazon S3 returns the version ID of the copied object in the\n x-amz-version-id response header in the response.

\n

If you do not enable versioning or suspend it on the target bucket, the version ID that\n Amazon S3 generates is always null.

\n
\n
\n

The following operations are related to CopyObject:

\n ", + "smithy.api#examples": [ + { + "title": "To copy an object", + "documentation": "The following example copies an object from one bucket to another.", + "input": { + "Bucket": "destinationbucket", + "CopySource": "/sourcebucket/HappyFacejpg", + "Key": "HappyFaceCopyjpg" + }, + "output": { + "CopyObjectResult": { + "LastModified": "2016-12-15T17:38:53.000Z", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"" + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=CopyObject", @@ -20377,7 +21882,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -20398,7 +21903,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -20413,7 +21918,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the copied object uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the copied object uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20441,7 +21946,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the destination bucket.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the destination bucket.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -20537,14 +22042,14 @@ "GrantFullControl": { "target": "com.amazonaws.s3#GrantFullControl", "traits": { - "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-full-control" } }, "GrantRead": { "target": "com.amazonaws.s3#GrantRead", "traits": { - "smithy.api#documentation": "

Allows grantee to read the object data and its\n metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to read the object data and its metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-read" } }, @@ -20558,7 +22063,7 @@ "GrantWriteACP": { "target": "com.amazonaws.s3#GrantWriteACP", "traits": { - "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-write-acp" } }, @@ -20594,21 +22099,21 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", + "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-storage-class" } }, "WebsiteRedirectLocation": { "target": "com.amazonaws.s3#WebsiteRedirectLocation", "traits": { - "smithy.api#documentation": "

If the bucket is configured as a website, redirects requests for this object to another\n object in the same bucket or to an external URL. Amazon S3 stores the value of this header in\n the object metadata.

", + "smithy.api#documentation": "

If the bucket is configured as a website, redirects requests for this object to another\n object in the same bucket or to an external URL. Amazon S3 stores the value of this header in\n the object metadata. This value is unique to each object and is not copied when using the\n x-amz-metadata-directive header. Instead, you may opt to provide this\n header in combination with the directive.

", "smithy.api#httpHeader": "x-amz-website-redirect-location" } }, @@ -20636,14 +22141,14 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS key ID to use for object encryption. All GET and PUT requests for\n an object protected by Amazon Web Services KMS will fail if not made via SSL or using SigV4. For\n information about configuring using any of the officially supported Amazon Web Services SDKs and Amazon Web Services CLI,\n see Specifying the\n Signature Version in Request Authentication in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies the KMS key ID to use for object encryption. All GET and PUT requests for an\n object protected by KMS will fail if they're not made via SSL or using SigV4. For\n information about configuring any of the officially supported Amazon Web Services SDKs and Amazon Web Services CLI, see\n Specifying the\n Signature Version in Request Authentication in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, "SSEKMSEncryptionContext": { "target": "com.amazonaws.s3#SSEKMSEncryptionContext", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of this\n header is a base64-encoded UTF-8 string holding JSON with the encryption context key-value\n pairs.

", + "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of\n this header is a base64-encoded UTF-8 string holding JSON with the encryption context\n key-value pairs.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-context" } }, @@ -20651,7 +22156,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with server-side encryption using AWS KMS (SSE-KMS). Setting this header to true causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with a COPY action doesn’t affect bucket-level settings for S3 Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using Key Management Service (KMS) keys (SSE-KMS). Setting this header to\n true causes Amazon S3 to use an S3 Bucket Key for object encryption with\n SSE-KMS.

\n

Specifying this header with a COPY action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20735,7 +22240,7 @@ "ETag": { "target": "com.amazonaws.s3#ETag", "traits": { - "smithy.api#documentation": "

Returns the ETag of the new object. The ETag reflects only changes to the contents of an object, not its metadata.

" + "smithy.api#documentation": "

Returns the ETag of the new object. The ETag reflects only changes to the contents of an\n object, not its metadata.

" } }, "LastModified": { @@ -20870,7 +22375,19 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new S3 bucket. To create a bucket, you must register with Amazon S3 and have a\n valid Amazon Web Services Access Key ID to authenticate requests. Anonymous requests are never allowed to\n create buckets. By creating the bucket, you become the bucket owner.

\n

Not every string is an acceptable bucket name. For information about bucket naming\n restrictions, see Bucket naming rules.

\n

If you want to create an Amazon S3 on Outposts bucket, see Create Bucket.

\n

By default, the bucket is created in the US East (N. Virginia) Region. You can\n optionally specify a Region in the request body. You might choose a Region to optimize\n latency, minimize costs, or address regulatory requirements. For example, if you reside in\n Europe, you will probably find it advantageous to create buckets in the Europe (Ireland)\n Region. For more information, see Accessing a\n bucket.

\n \n

If you send your create bucket request to the s3.amazonaws.com endpoint,\n the request goes to the us-east-1 Region. Accordingly, the signature calculations in\n Signature Version 4 must use us-east-1 as the Region, even if the location constraint in\n the request specifies another Region where the bucket is to be created. If you create a\n bucket in a Region other than US East (N. Virginia), your application must be able to\n handle 307 redirect. For more information, see Virtual hosting of buckets.

\n
\n

\n Access control lists (ACLs)\n

\n

When creating a bucket using this operation, you can optionally configure the bucket ACL to specify the accounts or\n groups that should be granted specific permissions on the bucket.

\n \n

If your CreateBucket request sets bucket owner enforced for S3 Object Ownership and\n specifies a bucket ACL that provides access to an external Amazon Web Services account, your request\n fails with a 400 error and returns the\n InvalidBucketAclWithObjectOwnership error code. For more information,\n see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n

There are two ways to grant the appropriate permissions using the request headers.

\n
    \n
  • \n

    Specify a canned ACL using the x-amz-acl request header. Amazon S3\n supports a set of predefined ACLs, known as canned ACLs. Each\n canned ACL has a predefined set of grantees and permissions. For more information,\n see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly using the x-amz-grant-read,\n x-amz-grant-write, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and x-amz-grant-full-control\n headers. These headers map to the set of permissions Amazon S3 supports in an ACL. For\n more information, see Access control list\n (ACL) overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n \n

You can use either a canned ACL or specify access permissions explicitly. You cannot\n do both.

\n
\n

\n Permissions\n

\n

In addition to s3:CreateBucket, the following permissions are required when your CreateBucket includes specific headers:

\n
    \n
  • \n

    \n ACLs - If your CreateBucket request specifies ACL permissions and the ACL is public-read, public-read-write, \n authenticated-read, or if you specify access permissions explicitly through any other ACL, both \n s3:CreateBucket and s3:PutBucketAcl permissions are needed. If the ACL the \n CreateBucket request is private or doesn't specify any ACLs, only s3:CreateBucket permission is needed.

    \n
  • \n
  • \n

    \n Object Lock - If\n ObjectLockEnabledForBucket is set to true in your\n CreateBucket request,\n s3:PutBucketObjectLockConfiguration and\n s3:PutBucketVersioning permissions are required.

    \n
  • \n
  • \n

    \n S3 Object Ownership - If your CreateBucket\n request includes the the x-amz-object-ownership header,\n s3:PutBucketOwnershipControls permission is required.

    \n
  • \n
\n

The following operations are related to CreateBucket:

\n ", + "smithy.api#documentation": "

Creates a new S3 bucket. To create a bucket, you must register with Amazon S3 and have a\n valid Amazon Web Services Access Key ID to authenticate requests. Anonymous requests are never allowed to\n create buckets. By creating the bucket, you become the bucket owner.

\n

Not every string is an acceptable bucket name. For information about bucket naming\n restrictions, see Bucket naming\n rules.

\n

If you want to create an Amazon S3 on Outposts bucket, see Create Bucket.

\n

By default, the bucket is created in the US East (N. Virginia) Region. You can\n optionally specify a Region in the request body. You might choose a Region to optimize\n latency, minimize costs, or address regulatory requirements. For example, if you reside in\n Europe, you will probably find it advantageous to create buckets in the Europe (Ireland)\n Region. For more information, see Accessing a\n bucket.

\n \n

If you send your create bucket request to the s3.amazonaws.com endpoint,\n the request goes to the us-east-1 Region. Accordingly, the signature calculations in\n Signature Version 4 must use us-east-1 as the Region, even if the location constraint in\n the request specifies another Region where the bucket is to be created. If you create a\n bucket in a Region other than US East (N. Virginia), your application must be able to\n handle 307 redirect. For more information, see Virtual hosting of\n buckets.

\n
\n
\n
Permissions
\n
\n

In addition to s3:CreateBucket, the following permissions are required when\n your CreateBucket request includes specific headers:

\n
    \n
  • \n

    \n Access control lists (ACLs) - If your CreateBucket request\n specifies access control list (ACL) permissions and the ACL is public-read, public-read-write,\n authenticated-read, or if you specify access permissions explicitly through any other\n ACL, both s3:CreateBucket and s3:PutBucketAcl permissions\n are needed. If the ACL for the CreateBucket request is private or if the request doesn't\n specify any ACLs, only s3:CreateBucket permission is needed.

    \n
  • \n
  • \n

    \n Object Lock - If ObjectLockEnabledForBucket is set to true in your\n CreateBucket request,\n s3:PutBucketObjectLockConfiguration and\n s3:PutBucketVersioning permissions are required.

    \n
  • \n
  • \n

    \n S3 Object Ownership - If your CreateBucket request includes the x-amz-object-ownership header, then the\n s3:PutBucketOwnershipControls permission is required. By default, ObjectOwnership is set to BucketOWnerEnforced and ACLs are disabled. We recommend keeping\n ACLs disabled, except in uncommon use cases where you must control access for each object individually. If you want to change the ObjectOwnership setting, you can use the \n x-amz-object-ownership header in your CreateBucket request to set the ObjectOwnership setting of your choice.\n For more information about S3 Object Ownership, see Controlling object\n ownership in the Amazon S3 User Guide.

    \n
  • \n
  • \n

    \n S3 Block Public Access - If your specific use case requires granting public access to your S3 resources, you can disable Block Public Access. You can create a new bucket with Block Public Access enabled, then separately call the \n DeletePublicAccessBlock\n API. To use this operation, you must have the\n s3:PutBucketPublicAccessBlock permission. By default, all Block\n Public Access settings are enabled for new buckets. To avoid inadvertent exposure of\n your resources, we recommend keeping the S3 Block Public Access settings enabled. For more information about S3 Block Public Access, see Blocking public\n access to your Amazon S3 storage in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
\n \n

If your CreateBucket request sets BucketOwnerEnforced for Amazon S3 Object Ownership\n and specifies a bucket ACL that provides access to an external Amazon Web Services account, your request fails with a 400 error and returns the InvalidBucketAcLWithObjectOwnership error code. For more information,\n see Setting Object\n Ownership on an existing bucket in the Amazon S3 User Guide.

\n
\n

The following operations are related to CreateBucket:

\n ", + "smithy.api#examples": [ + { + "title": "To create a bucket ", + "documentation": "The following example creates a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Location": "/examplebucket" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}", @@ -20965,7 +22482,7 @@ "GrantWrite": { "target": "com.amazonaws.s3#GrantWrite", "traits": { - "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and overwrites of those objects.

", + "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and\n overwrites of those objects.

", "smithy.api#httpHeader": "x-amz-grant-write" } }, @@ -21004,7 +22521,22 @@ "target": "com.amazonaws.s3#CreateMultipartUploadOutput" }, "traits": { - "smithy.api#documentation": "

This action initiates a multipart upload and returns an upload ID. This upload ID is\n used to associate all of the parts in the specific multipart upload. You specify this\n upload ID in each of your subsequent upload part requests (see UploadPart). You also include this\n upload ID in the final request to either complete or abort the multipart upload\n request.

\n

For more information about multipart uploads, see Multipart Upload Overview.

\n

If you have configured a lifecycle rule to abort incomplete multipart uploads, the\n upload must complete within the number of days specified in the bucket lifecycle\n configuration. Otherwise, the incomplete multipart upload becomes eligible for an abort\n action and Amazon S3 aborts the multipart upload. For more information, see Aborting\n Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

\n

For information about the permissions required to use the multipart upload API, see\n Multipart Upload and\n Permissions.

\n

For request signing, multipart upload is just a series of regular requests. You initiate\n a multipart upload, send one or more requests to upload parts, and then complete the\n multipart upload process. You sign each request individually. There is nothing special\n about signing multipart upload requests. For more information about signing, see Authenticating\n Requests (Amazon Web Services Signature Version 4).

\n \n

After you initiate a multipart upload and upload one or more parts, to stop being\n charged for storing the uploaded parts, you must either complete or abort the multipart\n upload. Amazon S3 frees up the space used to store the parts and stop charging you for\n storing them only after you either complete or abort a multipart upload.

\n
\n

You can optionally request server-side encryption. For server-side encryption, Amazon S3\n encrypts your data as it writes it to disks in its data centers and decrypts it when you\n access it. You can provide your own encryption key, or use Amazon Web Services KMS keys or Amazon S3-managed encryption keys. If you choose to provide\n your own encryption key, the request headers you provide in UploadPart and UploadPartCopy requests must match the headers you used in the request to\n initiate the upload by using CreateMultipartUpload.

\n

To perform a multipart upload with encryption using an Amazon Web Services KMS key, the requester must\n have permission to the kms:Decrypt and kms:GenerateDataKey*\n actions on the key. These permissions are required because Amazon S3 must decrypt and read data\n from the encrypted file parts before it completes the multipart upload. For more\n information, see Multipart upload API\n and permissions in the Amazon S3 User Guide.

\n

If your Identity and Access Management (IAM) user or role is in the same Amazon Web Services account\n as the KMS key, then you must have these permissions on the key policy. If your IAM\n user or role belongs to a different account than the key, then you must have the\n permissions on both the key policy and your IAM user or role.

\n

For more information, see Protecting\n Data Using Server-Side Encryption.

\n
\n
Access Permissions
\n
\n

When copying an object, you can optionally specify the accounts or groups that\n should be granted specific permissions on the new object. There are two ways to\n grant the permissions using the request headers:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. For\n more information, see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the\n x-amz-grant-read, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. These parameters map to\n the set of permissions that Amazon S3 supports in an ACL. For more information,\n see Access Control List (ACL)\n Overview.

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You\n cannot do both.

\n
\n
Server-Side- Encryption-Specific Request Headers
\n
\n

You can optionally tell Amazon S3 to encrypt data at rest using server-side\n encryption. Server-side encryption is for data encryption at rest. Amazon S3 encrypts\n your data as it writes it to disks in its data centers and decrypts it when you\n access it. The option you use depends on whether you want to use Amazon Web Services managed\n encryption keys or provide your own encryption key.

\n
    \n
  • \n

    Use encryption keys managed by Amazon S3 or customer managed key stored\n in Amazon Web Services Key Management Service (Amazon Web Services KMS) – If you want Amazon Web Services to manage the keys\n used to encrypt data, specify the following headers in the request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-aws-kms-key-id\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-context\n

      \n
    • \n
    \n \n

    If you specify x-amz-server-side-encryption:aws:kms, but\n don't provide x-amz-server-side-encryption-aws-kms-key-id,\n Amazon S3 uses the Amazon Web Services managed key in Amazon Web Services KMS to protect the data.

    \n
    \n \n

    All GET and PUT requests for an object protected by Amazon Web Services KMS fail if\n you don't make them with SSL or by using SigV4.

    \n
    \n

    For more information about server-side encryption with KMS key (SSE-KMS),\n see Protecting Data Using Server-Side Encryption with KMS keys.

    \n
  • \n
  • \n

    Use customer-provided encryption keys – If you want to manage your own\n encryption keys, provide all the following headers in the request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption-customer-algorithm\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key-MD5\n

      \n
    • \n
    \n

    For more information about server-side encryption with KMS keys (SSE-KMS),\n see Protecting Data Using Server-Side Encryption with KMS keys.

    \n
  • \n
\n
\n
Access-Control-List (ACL)-Specific Request Headers
\n
\n

You also can use the following access control–related headers with this\n operation. By default, all objects are private. Only the owner has full access\n control. When adding a new object, you can grant permissions to individual Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are then added\n to the access control list (ACL) on the object. For more information, see Using ACLs. With this\n operation, you can grant access permissions using one of the following two\n methods:

\n
    \n
  • \n

    Specify a canned ACL (x-amz-acl) — Amazon S3 supports a set of\n predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. For more information, see\n Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly — To explicitly grant access\n permissions to specific Amazon Web Services accounts or groups, use the following headers.\n Each header maps to specific permissions that Amazon S3 supports in an ACL. For\n more information, see Access\n Control List (ACL) Overview. In the header, you specify a list of\n grantees who get the specific permission. To grant permissions explicitly,\n use:

    \n
      \n
    • \n

      \n x-amz-grant-read\n

      \n
    • \n
    • \n

      \n x-amz-grant-write\n

      \n
    • \n
    • \n

      \n x-amz-grant-read-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-write-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-full-control\n

      \n
    • \n
    \n

    You specify each grantee as a type=value pair, where the type is one of\n the following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID\n of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email\n address of an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n
\n
\n

The following operations are related to CreateMultipartUpload:

\n ", + "smithy.api#documentation": "

This action initiates a multipart upload and returns an upload ID. This upload ID is\n used to associate all of the parts in the specific multipart upload. You specify this\n upload ID in each of your subsequent upload part requests (see UploadPart). You also include this\n upload ID in the final request to either complete or abort the multipart upload\n request.

\n

For more information about multipart uploads, see Multipart Upload Overview.

\n

If you have configured a lifecycle rule to abort incomplete multipart uploads, the\n upload must complete within the number of days specified in the bucket lifecycle\n configuration. Otherwise, the incomplete multipart upload becomes eligible for an abort\n action and Amazon S3 aborts the multipart upload. For more information, see Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration.

\n

For information about the permissions required to use the multipart upload API, see\n Multipart\n Upload and Permissions.

\n

For request signing, multipart upload is just a series of regular requests. You initiate\n a multipart upload, send one or more requests to upload parts, and then complete the\n multipart upload process. You sign each request individually. There is nothing special\n about signing multipart upload requests. For more information about signing, see Authenticating Requests (Amazon Web Services Signature Version 4).

\n \n

After you initiate a multipart upload and upload one or more parts, to stop being\n charged for storing the uploaded parts, you must either complete or abort the multipart\n upload. Amazon S3 frees up the space used to store the parts and stop charging you for\n storing them only after you either complete or abort a multipart upload.

\n
\n

Server-side encryption is for data encryption at rest. Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts it when you access it. Amazon S3\n automatically encrypts all new objects that are uploaded to an S3 bucket. When doing a\n multipart upload, if you don't specify encryption information in your request, the\n encryption setting of the uploaded parts is set to the default encryption configuration of\n the destination bucket. By default, all buckets have a base level of encryption\n configuration that uses server-side encryption with Amazon S3 managed keys (SSE-S3). If the\n destination bucket has a default encryption configuration that uses server-side encryption\n with an Key Management Service (KMS) key (SSE-KMS), or a customer-provided encryption key (SSE-C),\n Amazon S3 uses the corresponding KMS key, or a customer-provided key to encrypt the uploaded\n parts. When you perform a CreateMultipartUpload operation, if you want to use a different\n type of encryption setting for the uploaded parts, you can request that Amazon S3 encrypts the\n object with a KMS key, an Amazon S3 managed key, or a customer-provided key. If the encryption\n setting in your request is different from the default encryption configuration of the\n destination bucket, the encryption setting in your request takes precedence. If you choose\n to provide your own encryption key, the request headers you provide in UploadPart\n and UploadPartCopy requests must match the headers you used in the request to\n initiate the upload by using CreateMultipartUpload. You can request that Amazon S3\n save the uploaded parts encrypted with server-side encryption with an Amazon S3 managed key\n (SSE-S3), an Key Management Service (KMS) key (SSE-KMS), or a customer-provided encryption key\n (SSE-C).

\n

To perform a multipart upload with encryption by using an Amazon Web Services KMS key, the requester\n must have permission to the kms:Decrypt and kms:GenerateDataKey*\n actions on the key. These permissions are required because Amazon S3 must decrypt and read data\n from the encrypted file parts before it completes the multipart upload. For more\n information, see Multipart upload API\n and permissions and Protecting data using\n server-side encryption with Amazon Web Services KMS in the\n Amazon S3 User Guide.

\n

If your Identity and Access Management (IAM) user or role is in the same Amazon Web Services account as the KMS key,\n then you must have these permissions on the key policy. If your IAM user or role belongs\n to a different account than the key, then you must have the permissions on both the key\n policy and your IAM user or role.

\n

For more information, see Protecting Data Using Server-Side\n Encryption.

\n
\n
Access Permissions
\n
\n

When copying an object, you can optionally specify the accounts or groups that\n should be granted specific permissions on the new object. There are two ways to\n grant the permissions using the request headers:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. For\n more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the\n x-amz-grant-read, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. These parameters map to\n the set of permissions that Amazon S3 supports in an ACL. For more information,\n see Access Control List (ACL) Overview.

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You\n cannot do both.

\n
\n
Server-Side- Encryption-Specific Request Headers
\n
\n

Amazon S3 encrypts data\n by using server-side encryption with an Amazon S3 managed key (SSE-S3) by default. Server-side encryption is for data encryption at rest. Amazon S3 encrypts\n your data as it writes it to disks in its data centers and decrypts it when you\n access it. You can request that Amazon S3 encrypts\n data at rest by using server-side encryption with other key options. The option you use depends on\n whether you want to use KMS keys (SSE-KMS) or provide your own encryption keys\n (SSE-C).

\n
    \n
  • \n

    Use KMS keys (SSE-KMS) that include the Amazon Web Services managed key\n (aws/s3) and KMS customer managed keys stored in Key Management Service (KMS) – If you\n want Amazon Web Services to manage the keys used to encrypt data, specify the following\n headers in the request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-aws-kms-key-id\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-context\n

      \n
    • \n
    \n \n

    If you specify x-amz-server-side-encryption:aws:kms, but\n don't provide x-amz-server-side-encryption-aws-kms-key-id,\n Amazon S3 uses the Amazon Web Services managed key (aws/s3 key) in KMS to\n protect the data.

    \n
    \n \n

    All GET and PUT requests for an object protected\n by KMS fail if you don't make them by using Secure Sockets Layer (SSL),\n Transport Layer Security (TLS), or Signature Version 4.

    \n
    \n

    For more information about server-side encryption with KMS keys\n (SSE-KMS), see Protecting Data\n Using Server-Side Encryption with KMS keys.

    \n
  • \n
  • \n

    Use customer-provided encryption keys (SSE-C) – If you want to manage\n your own encryption keys, provide all the following headers in the\n request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption-customer-algorithm\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key-MD5\n

      \n
    • \n
    \n

    For more information about server-side encryption with customer-provided\n encryption keys (SSE-C), see \n Protecting data using server-side encryption with customer-provided\n encryption keys (SSE-C).

    \n
  • \n
\n
\n
Access-Control-List (ACL)-Specific Request Headers
\n
\n

You also can use the following access control–related headers with this\n operation. By default, all objects are private. Only the owner has full access\n control. When adding a new object, you can grant permissions to individual\n Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are then\n added to the access control list (ACL) on the object. For more information, see\n Using ACLs. With this operation, you can grant access permissions\n using one of the following two methods:

\n
    \n
  • \n

    Specify a canned ACL (x-amz-acl) — Amazon S3 supports a set of\n predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. For more information, see\n Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly — To explicitly grant access\n permissions to specific Amazon Web Services accounts or groups, use the following headers.\n Each header maps to specific permissions that Amazon S3 supports in an ACL. For\n more information, see Access Control List (ACL)\n Overview. In the header, you specify a list of grantees who get\n the specific permission. To grant permissions explicitly, use:

    \n
      \n
    • \n

      \n x-amz-grant-read\n

      \n
    • \n
    • \n

      \n x-amz-grant-write\n

      \n
    • \n
    • \n

      \n x-amz-grant-read-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-write-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-full-control\n

      \n
    • \n
    \n

    You specify each grantee as a type=value pair, where the type is one of\n the following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID\n of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email\n address of an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n
\n
\n

The following operations are related to CreateMultipartUpload:

\n ", + "smithy.api#examples": [ + { + "title": "To initiate a multipart upload", + "documentation": "The following example initiates a multipart upload.", + "input": { + "Bucket": "examplebucket", + "Key": "largeobject" + }, + "output": { + "Bucket": "examplebucket", + "UploadId": "ibZBv_75gd9r8lH_gqXatLdxMVpAlj6ZQjEs.OwyF3953YdwbcQnMA2BLGn8Lx12fQNICtMw5KyteFeHw.Sjng--", + "Key": "largeobject" + } + } + ], "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?uploads&x-id=CreateMultipartUpload", @@ -21018,7 +22550,7 @@ "AbortDate": { "target": "com.amazonaws.s3#AbortDate", "traits": { - "smithy.api#documentation": "

If the bucket has a lifecycle rule configured with an action to abort incomplete\n multipart uploads and the prefix in the lifecycle rule matches the object name in the\n request, the response includes this header. The header indicates when the initiated\n multipart upload becomes eligible for an abort operation. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

\n

The response also includes the x-amz-abort-rule-id header that provides the\n ID of the lifecycle configuration rule that defines this action.

", + "smithy.api#documentation": "

If the bucket has a lifecycle rule configured with an action to abort incomplete\n multipart uploads and the prefix in the lifecycle rule matches the object name in the\n request, the response includes this header. The header indicates when the initiated\n multipart upload becomes eligible for an abort operation. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration.

\n

The response also includes the x-amz-abort-rule-id header that provides the\n ID of the lifecycle configuration rule that defines this action.

", "smithy.api#httpHeader": "x-amz-abort-date" } }, @@ -21032,7 +22564,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the access point ARN or access point alias if used.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the\n access point ARN or access point alias if used.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#xmlName": "Bucket" } }, @@ -21051,7 +22583,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -21072,7 +22604,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -21087,7 +22619,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -21123,7 +22655,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which to initiate the upload

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which to initiate the upload

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -21176,14 +22708,14 @@ "GrantFullControl": { "target": "com.amazonaws.s3#GrantFullControl", "traits": { - "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-full-control" } }, "GrantRead": { "target": "com.amazonaws.s3#GrantRead", "traits": { - "smithy.api#documentation": "

Allows grantee to read the object data and its\n metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to read the object data and its metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-read" } }, @@ -21197,7 +22729,7 @@ "GrantWriteACP": { "target": "com.amazonaws.s3#GrantWriteACP", "traits": { - "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-write-acp" } }, @@ -21219,14 +22751,14 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", + "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-storage-class" } }, @@ -21261,14 +22793,14 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the ID of the symmetric customer managed key to use for object\n encryption. All GET and PUT requests for an object protected by Amazon Web Services KMS will fail if not\n made via SSL or using SigV4. For information about configuring using any of the officially\n supported Amazon Web Services SDKs and Amazon Web Services CLI, see Specifying the Signature Version in Request Authentication\n in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies the ID of the symmetric encryption customer managed key to use for object encryption.\n All GET and PUT requests for an object protected by KMS will fail if they're not made via\n SSL or using SigV4. For information about configuring any of the officially supported Amazon Web Services\n SDKs and Amazon Web Services CLI, see Specifying the Signature Version in Request Authentication\n in the Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, "SSEKMSEncryptionContext": { "target": "com.amazonaws.s3#SSEKMSEncryptionContext", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of this\n header is a base64-encoded UTF-8 string holding JSON with the encryption context key-value\n pairs.

", + "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of\n this header is a base64-encoded UTF-8 string holding JSON with the encryption context\n key-value pairs.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-context" } }, @@ -21276,7 +22808,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with server-side encryption using AWS KMS (SSE-KMS). Setting this header to true causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with an object action doesn’t affect bucket-level settings for S3 Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using Key Management Service (KMS) keys (SSE-KMS). Setting this header to\n true causes Amazon S3 to use an S3 Bucket Key for object encryption with\n SSE-KMS.

\n

Specifying this header with an object action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -21379,7 +22911,7 @@ } }, "traits": { - "smithy.api#documentation": "

The container element for specifying the default Object Lock retention settings for new\n objects placed in the specified bucket.

\n \n
    \n
  • \n

    The DefaultRetention settings require both a mode and a\n period.

    \n
  • \n
  • \n

    The DefaultRetention period can be either Days\n or Years but you must select one. You cannot specify Days\n and Years at the same time.

    \n
  • \n
\n
" + "smithy.api#documentation": "

The container element for specifying the default Object Lock retention settings for new\n objects placed in the specified bucket.

\n \n
    \n
  • \n

    The DefaultRetention settings require both a mode and a\n period.

    \n
  • \n
  • \n

    The DefaultRetention period can be either Days or\n Years but you must select one. You cannot specify\n Days and Years at the same time.

    \n
  • \n
\n
" } }, "com.amazonaws.s3#Delete": { @@ -21388,7 +22920,7 @@ "Objects": { "target": "com.amazonaws.s3#ObjectIdentifierList", "traits": { - "smithy.api#documentation": "

The objects to delete.

", + "smithy.api#documentation": "

The object to delete.

", "smithy.api#required": {}, "smithy.api#xmlFlattened": {}, "smithy.api#xmlName": "Object" @@ -21415,7 +22947,16 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the S3 bucket. All objects (including all object versions and delete markers) in\n the bucket must be deleted before the bucket itself can be deleted.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Deletes the S3 bucket. All objects (including all object versions and delete markers) in\n the bucket must be deleted before the bucket itself can be deleted.

\n

The following operations are related to DeleteBucket:

\n ", + "smithy.api#examples": [ + { + "title": "To delete a bucket", + "documentation": "The following example deletes the specified bucket.", + "input": { + "Bucket": "forrandall2" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}", @@ -21432,7 +22973,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes an analytics configuration for the bucket (specified by the analytics\n configuration ID).

\n

To use this operation, you must have permissions to perform the\n s3:PutAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about the Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis.

\n

The following operations are related to\n DeleteBucketAnalyticsConfiguration:

\n ", + "smithy.api#documentation": "

Deletes an analytics configuration for the bucket (specified by the analytics\n configuration ID).

\n

To use this operation, you must have permissions to perform the\n s3:PutAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about the Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis.

\n

The following operations are related to\n DeleteBucketAnalyticsConfiguration:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?analytics", @@ -21483,7 +23024,16 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the cors configuration information set for the bucket.

\n

To use this operation, you must have permission to perform the\n s3:PutBucketCORS action. The bucket owner has this permission by default\n and can grant this permission to others.

\n

For information about cors, see Enabling\n Cross-Origin Resource Sharing in the Amazon S3 User Guide.

\n

\n Related Resources:\n

\n ", + "smithy.api#documentation": "

Deletes the cors configuration information set for the bucket.

\n

To use this operation, you must have permission to perform the\n s3:PutBucketCORS action. The bucket owner has this permission by default\n and can grant this permission to others.

\n

For information about cors, see Enabling Cross-Origin Resource Sharing in\n the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#examples": [ + { + "title": "To delete cors configuration on a bucket.", + "documentation": "The following example deletes CORS configuration on a bucket.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?cors", @@ -21526,7 +23076,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

This implementation of the DELETE action removes default encryption from the bucket.\n For information about the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption in the\n Amazon S3 User Guide.

\n

To use this operation, you must have permissions to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the DELETE action resets the default encryption for the\n bucket as server-side encryption with Amazon S3 managed keys (SSE-S3). For information about the\n bucket default encryption feature, see Amazon S3 Bucket Default Encryption\n in the Amazon S3 User Guide.

\n

To use this operation, you must have permissions to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketEncryption:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?encryption", @@ -21569,7 +23119,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to\n DeleteBucketIntelligentTieringConfiguration include:

\n ", + "smithy.api#documentation": "

Deletes the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to DeleteBucketIntelligentTieringConfiguration include:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?intelligent-tiering", @@ -21613,7 +23163,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes an inventory configuration (identified by the inventory ID) from the\n bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutInventoryConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

\n

Operations related to DeleteBucketInventoryConfiguration include:

\n ", + "smithy.api#documentation": "

Deletes an inventory configuration (identified by the inventory ID) from the\n bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutInventoryConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

\n

Operations related to DeleteBucketInventoryConfiguration include:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?inventory", @@ -21664,7 +23214,16 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the lifecycle configuration from the specified bucket. Amazon S3 removes all the\n lifecycle configuration rules in the lifecycle subresource associated with the bucket. Your\n objects never expire, and Amazon S3 no longer automatically deletes any objects on the basis of\n rules contained in the deleted lifecycle configuration.

\n

To use this operation, you must have permission to perform the\n s3:PutLifecycleConfiguration action. By default, the bucket owner has this\n permission and the bucket owner can grant this permission to others.

\n

There is usually some time lag before lifecycle configuration deletion is fully\n propagated to all the Amazon S3 systems.

\n

For more information about the object expiration, see Elements to\n Describe Lifecycle Actions.

\n

Related actions include:

\n ", + "smithy.api#documentation": "

Deletes the lifecycle configuration from the specified bucket. Amazon S3 removes all the\n lifecycle configuration rules in the lifecycle subresource associated with the bucket. Your\n objects never expire, and Amazon S3 no longer automatically deletes any objects on the basis of\n rules contained in the deleted lifecycle configuration.

\n

To use this operation, you must have permission to perform the\n s3:PutLifecycleConfiguration action. By default, the bucket owner has this\n permission and the bucket owner can grant this permission to others.

\n

There is usually some time lag before lifecycle configuration deletion is fully\n propagated to all the Amazon S3 systems.

\n

For more information about the object expiration, see Elements to Describe Lifecycle Actions.

\n

Related actions include:

\n ", + "smithy.api#examples": [ + { + "title": "To delete lifecycle configuration on a bucket.", + "documentation": "The following example deletes lifecycle configuration on a bucket.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?lifecycle", @@ -21707,7 +23266,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes a metrics configuration for the Amazon CloudWatch request metrics (specified by the\n metrics configuration ID) from the bucket. Note that this doesn't include the daily storage\n metrics.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon CloudWatch.

\n

The following operations are related to\n DeleteBucketMetricsConfiguration:

\n ", + "smithy.api#documentation": "

Deletes a metrics configuration for the Amazon CloudWatch request metrics (specified by the\n metrics configuration ID) from the bucket. Note that this doesn't include the daily storage\n metrics.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with\n Amazon CloudWatch.

\n

The following operations are related to\n DeleteBucketMetricsConfiguration:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?metrics", @@ -21732,7 +23291,7 @@ "Id": { "target": "com.amazonaws.s3#MetricsId", "traits": { - "smithy.api#documentation": "

The ID used to identify the metrics configuration.

", + "smithy.api#documentation": "

The ID used to identify the metrics configuration. The ID has a 64 character limit and\n can only contain letters, numbers, periods, dashes, and underscores.

", "smithy.api#httpQuery": "id", "smithy.api#required": {} } @@ -21758,7 +23317,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Removes OwnershipControls for an Amazon S3 bucket. To use this operation, you\n must have the s3:PutBucketOwnershipControls permission. For more information\n about Amazon S3 permissions, see Specifying\n Permissions in a Policy.

\n

For information about Amazon S3 Object Ownership, see Using Object Ownership.

\n

The following operations are related to\n DeleteBucketOwnershipControls:

\n ", + "smithy.api#documentation": "

Removes OwnershipControls for an Amazon S3 bucket. To use this operation, you\n must have the s3:PutBucketOwnershipControls permission. For more information\n about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n

For information about Amazon S3 Object Ownership, see Using Object Ownership.

\n

The following operations are related to\n DeleteBucketOwnershipControls:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?ownershipControls", @@ -21801,7 +23360,16 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

This implementation of the DELETE action uses the policy subresource to delete the\n policy of a specified bucket. If you are using an identity other than the root user of the\n Amazon Web Services account that owns the bucket, the calling identity must have the\n DeleteBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account to use this operation.

\n

If you don't have DeleteBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

As a security precaution, the root user of the Amazon Web Services account that owns a bucket can\n always use this operation, even if the policy explicitly denies the root user the\n ability to perform this action.

\n
\n

For more information about bucket policies, see Using Bucket Policies and\n UserPolicies.

\n

The following operations are related to DeleteBucketPolicy\n

\n ", + "smithy.api#documentation": "

This implementation of the DELETE action uses the policy subresource to delete the\n policy of a specified bucket. If you are using an identity other than the root user of the\n Amazon Web Services account that owns the bucket, the calling identity must have the\n DeleteBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account to use this operation.

\n

If you don't have DeleteBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

For more information about bucket policies, see Using Bucket Policies and\n UserPolicies.

\n

The following operations are related to DeleteBucketPolicy\n

\n ", + "smithy.api#examples": [ + { + "title": "To delete bucket policy", + "documentation": "The following example deletes bucket policy on the specified bucket.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?policy", @@ -21844,7 +23412,16 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the replication configuration from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutReplicationConfiguration action. The bucket owner has these\n permissions by default and can grant it to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n \n

It can take a while for the deletion of a replication configuration to fully\n propagate.

\n
\n

For information about replication configuration, see Replication in the Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", + "smithy.api#documentation": "

Deletes the replication configuration from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutReplicationConfiguration action. The bucket owner has these\n permissions by default and can grant it to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n \n

It can take a while for the deletion of a replication configuration to fully\n propagate.

\n
\n

For information about replication configuration, see Replication in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", + "smithy.api#examples": [ + { + "title": "To delete bucket replication configuration", + "documentation": "The following example deletes replication configuration set on bucket.", + "input": { + "Bucket": "example" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?replication", @@ -21914,6 +23491,15 @@ }, "traits": { "smithy.api#documentation": "

Deletes the tags from the bucket.

\n

To use this operation, you must have permission to perform the\n s3:PutBucketTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

The following operations are related to DeleteBucketTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To delete bucket tags", + "documentation": "The following example deletes bucket tags.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?tagging", @@ -21957,6 +23543,15 @@ }, "traits": { "smithy.api#documentation": "

This action removes the website configuration for a bucket. Amazon S3 returns a 200\n OK response upon successfully deleting a website configuration on the specified\n bucket. You will get a 200 OK response if the website configuration you are\n trying to delete does not exist on the bucket. Amazon S3 returns a 404 response if\n the bucket specified in the request does not exist.

\n

This DELETE action requires the S3:DeleteBucketWebsite permission. By\n default, only the bucket owner can delete the website configuration attached to a bucket.\n However, bucket owners can grant other users permission to delete the website configuration\n by writing a bucket policy granting them the S3:DeleteBucketWebsite\n permission.

\n

For more information about hosting websites, see Hosting Websites on Amazon S3.

\n

The following operations are related to DeleteBucketWebsite:

\n ", + "smithy.api#examples": [ + { + "title": "To delete bucket website configuration", + "documentation": "The following example deletes bucket website configuration.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?website", @@ -22084,7 +23679,18 @@ "target": "com.amazonaws.s3#DeleteObjectOutput" }, "traits": { - "smithy.api#documentation": "

Removes the null version (if there is one) of an object and inserts a delete marker,\n which becomes the latest version of the object. If there isn't a null version, Amazon S3 does\n not remove any objects but will still respond that the command was successful.

\n

To remove a specific version, you must be the bucket owner and you must use the version\n Id subresource. Using this subresource permanently deletes the version. If the object\n deleted is a delete marker, Amazon S3 sets the response header,\n x-amz-delete-marker, to true.

\n

If the object you want to delete is in a bucket where the bucket versioning\n configuration is MFA Delete enabled, you must include the x-amz-mfa request\n header in the DELETE versionId request. Requests that include\n x-amz-mfa must use HTTPS.

\n

For more information about MFA Delete, see Using MFA Delete. To see sample requests that use versioning, see Sample Request.

\n

You can delete objects by explicitly calling DELETE Object or configure its\n lifecycle (PutBucketLifecycle) to\n enable Amazon S3 to remove them for you. If you want to block users or accounts from removing or\n deleting objects from your bucket, you must deny them the s3:DeleteObject,\n s3:DeleteObjectVersion, and s3:PutLifeCycleConfiguration\n actions.

\n

The following action is related to DeleteObject:

\n ", + "smithy.api#documentation": "

Removes the null version (if there is one) of an object and inserts a delete marker,\n which becomes the latest version of the object. If there isn't a null version, Amazon S3 does\n not remove any objects but will still respond that the command was successful.

\n

To remove a specific version, you must use the version Id subresource. Using this\n subresource permanently deletes the version. If the object deleted is a delete marker, Amazon S3\n sets the response header, x-amz-delete-marker, to true.

\n

If the object you want to delete is in a bucket where the bucket versioning\n configuration is MFA Delete enabled, you must include the x-amz-mfa request\n header in the DELETE versionId request. Requests that include\n x-amz-mfa must use HTTPS.

\n

For more information about MFA Delete, see Using MFA Delete. To see sample\n requests that use versioning, see Sample\n Request.

\n

You can delete objects by explicitly calling DELETE Object or configure its lifecycle\n (PutBucketLifecycle) to enable Amazon S3 to remove them for you. If you want to block\n users or accounts from removing or deleting objects from your bucket, you must deny them\n the s3:DeleteObject, s3:DeleteObjectVersion, and\n s3:PutLifeCycleConfiguration actions.

\n

The following action is related to DeleteObject:

\n ", + "smithy.api#examples": [ + { + "title": "To delete an object", + "documentation": "The following example deletes an object from an S3 bucket.", + "input": { + "Bucket": "examplebucket", + "Key": "objectkey.jpg" + }, + "output": {} + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?x-id=DeleteObject", @@ -22127,7 +23733,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name of the bucket containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name of the bucket containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -22167,7 +23773,7 @@ "target": "com.amazonaws.s3#BypassGovernanceRetention", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether S3 Object Lock should bypass Governance-mode restrictions to process\n this operation. To use this header, you must have the s3:BypassGovernanceRetention\n permission.

", + "smithy.api#documentation": "

Indicates whether S3 Object Lock should bypass Governance-mode restrictions to process\n this operation. To use this header, you must have the\n s3:BypassGovernanceRetention permission.

", "smithy.api#httpHeader": "x-amz-bypass-governance-retention" } }, @@ -22192,7 +23798,21 @@ "target": "com.amazonaws.s3#DeleteObjectTaggingOutput" }, "traits": { - "smithy.api#documentation": "

Removes the entire tag set from the specified object. For more information about\n managing object tags, see Object\n Tagging.

\n

To use this operation, you must have permission to perform the\n s3:DeleteObjectTagging action.

\n

To delete tags of a specific object version, add the versionId query\n parameter in the request. You will need permission for the\n s3:DeleteObjectVersionTagging action.

\n

The following operations are related to\n DeleteBucketMetricsConfiguration:

\n ", + "smithy.api#documentation": "

Removes the entire tag set from the specified object. For more information about\n managing object tags, see Object Tagging.

\n

To use this operation, you must have permission to perform the\n s3:DeleteObjectTagging action.

\n

To delete tags of a specific object version, add the versionId query\n parameter in the request. You will need permission for the\n s3:DeleteObjectVersionTagging action.

\n

The following operations are related to DeleteObjectTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To remove tag set from an object version", + "documentation": "The following example removes tag set associated with the specified object version. The request specifies both the object key and object version.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + }, + "output": { + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?tagging", @@ -22221,7 +23841,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the objects from which to remove the tags.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the objects from which to remove the tags.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -22269,7 +23889,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

This action enables you to delete multiple objects from a bucket using a single HTTP\n request. If you know the object keys that you want to delete, then this action provides\n a suitable alternative to sending individual delete requests, reducing per-request\n overhead.

\n

The request contains a list of up to 1000 keys that you want to delete. In the XML, you\n provide the object key names, and optionally, version IDs if you want to delete a specific\n version of the object from a versioning-enabled bucket. For each key, Amazon S3 performs a\n delete action and returns the result of that delete, success, or failure, in the\n response. Note that if the object specified in the request is not found, Amazon S3 returns the\n result as deleted.

\n

The action supports two modes for the response: verbose and quiet. By default, the\n action uses verbose mode in which the response includes the result of deletion of each\n key in your request. In quiet mode the response includes only keys where the delete\n action encountered an error. For a successful deletion, the action does not return\n any information about the delete in the response body.

\n

When performing this action on an MFA Delete enabled bucket, that attempts to delete\n any versioned objects, you must include an MFA token. If you do not provide one, the entire\n request will fail, even if there are non-versioned objects you are trying to delete. If you\n provide an invalid token, whether there are versioned keys in the request or not, the\n entire Multi-Object Delete request will fail. For information about MFA Delete, see MFA\n Delete.

\n

Finally, the Content-MD5 header is required for all Multi-Object Delete requests. Amazon\n S3 uses the header value to ensure that your request body has not been altered in\n transit.

\n

The following operations are related to DeleteObjects:

\n ", + "smithy.api#documentation": "

This action enables you to delete multiple objects from a bucket using a single HTTP\n request. If you know the object keys that you want to delete, then this action provides a\n suitable alternative to sending individual delete requests, reducing per-request\n overhead.

\n

The request contains a list of up to 1000 keys that you want to delete. In the XML, you\n provide the object key names, and optionally, version IDs if you want to delete a specific\n version of the object from a versioning-enabled bucket. For each key, Amazon S3 performs a\n delete action and returns the result of that delete, success, or failure, in the response.\n Note that if the object specified in the request is not found, Amazon S3 returns the result as\n deleted.

\n

The action supports two modes for the response: verbose and quiet. By default, the\n action uses verbose mode in which the response includes the result of deletion of each key\n in your request. In quiet mode the response includes only keys where the delete action\n encountered an error. For a successful deletion, the action does not return any information\n about the delete in the response body.

\n

When performing this action on an MFA Delete enabled bucket, that attempts to delete any\n versioned objects, you must include an MFA token. If you do not provide one, the entire\n request will fail, even if there are non-versioned objects you are trying to delete. If you\n provide an invalid token, whether there are versioned keys in the request or not, the\n entire Multi-Object Delete request will fail. For information about MFA Delete, see MFA\n Delete.

\n

Finally, the Content-MD5 header is required for all Multi-Object Delete requests. Amazon S3 uses the header value to ensure that your request body has not been altered in\n transit.

\n

The following operations are related to DeleteObjects:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}?delete&x-id=DeleteObjects", @@ -22313,7 +23933,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the objects to delete.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the objects to delete.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -22347,7 +23967,7 @@ "target": "com.amazonaws.s3#BypassGovernanceRetention", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether you want to delete this object even if it has a Governance-type Object\n Lock in place. To use this header, you must have the s3:BypassGovernanceRetention\n permission.

", + "smithy.api#documentation": "

Specifies whether you want to delete this object even if it has a Governance-type Object\n Lock in place. To use this header, you must have the\n s3:BypassGovernanceRetention permission.

", "smithy.api#httpHeader": "x-amz-bypass-governance-retention" } }, @@ -22361,7 +23981,7 @@ "ChecksumAlgorithm": { "target": "com.amazonaws.s3#ChecksumAlgorithm", "traits": { - "smithy.api#documentation": "

Indicates the algorithm used to create the checksum for the object when using the SDK. This header will not provide any\n additional functionality if not using the SDK. When sending this header, there must be a corresponding x-amz-checksum or\n x-amz-trailer header sent. Otherwise, Amazon S3 fails the request with the HTTP status code 400 Bad Request. For more\n information, see Checking object integrity in\n the Amazon S3 User Guide.

\n

If you provide an individual checksum, Amazon S3 ignores any provided\n ChecksumAlgorithm parameter.

\n

This checksum algorithm must be the same for all parts and it match the checksum\n value supplied in the CreateMultipartUpload request.

", + "smithy.api#documentation": "

Indicates the algorithm used to create the checksum for the object when using the SDK. This header will not provide any\n additional functionality if not using the SDK. When sending this header, there must be a corresponding x-amz-checksum or\n x-amz-trailer header sent. Otherwise, Amazon S3 fails the request with the HTTP status code 400 Bad Request. For more\n information, see Checking object integrity in\n the Amazon S3 User Guide.

\n

If you provide an individual checksum, Amazon S3 ignores any provided\n ChecksumAlgorithm parameter.

\n

This checksum algorithm must be the same for all parts and it match the checksum value\n supplied in the CreateMultipartUpload request.

", "smithy.api#httpHeader": "x-amz-sdk-checksum-algorithm" } } @@ -22379,7 +23999,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Removes the PublicAccessBlock configuration for an Amazon S3 bucket. To use this\n operation, you must have the s3:PutBucketPublicAccessBlock permission. For\n more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

The following operations are related to DeletePublicAccessBlock:

\n ", + "smithy.api#documentation": "

Removes the PublicAccessBlock configuration for an Amazon S3 bucket. To use this\n operation, you must have the s3:PutBucketPublicAccessBlock permission. For\n more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The following operations are related to DeletePublicAccessBlock:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?publicAccessBlock", @@ -22464,14 +24084,14 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the bucket where you want Amazon S3 to store the results.

", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the bucket where you want Amazon S3 to store the\n results.

", "smithy.api#required": {} } }, "Account": { "target": "com.amazonaws.s3#AccountId", "traits": { - "smithy.api#documentation": "

Destination bucket owner account ID. In a cross-account scenario, if you direct Amazon S3 to\n change replica ownership to the Amazon Web Services account that owns the destination bucket by specifying\n the AccessControlTranslation property, this is the account ID of the\n destination bucket owner. For more information, see Replication Additional\n Configuration: Changing the Replica Owner in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Destination bucket owner account ID. In a cross-account scenario, if you direct Amazon S3 to\n change replica ownership to the Amazon Web Services account that owns the destination bucket by\n specifying the AccessControlTranslation property, this is the account ID of\n the destination bucket owner. For more information, see Replication Additional\n Configuration: Changing the Replica Owner in the\n Amazon S3 User Guide.

" } }, "StorageClass": { @@ -22483,7 +24103,7 @@ "AccessControlTranslation": { "target": "com.amazonaws.s3#AccessControlTranslation", "traits": { - "smithy.api#documentation": "

Specify this only in a cross-account scenario (where source and destination bucket\n owners are not the same), and you want to change replica ownership to the Amazon Web Services account that\n owns the destination bucket. If this is not specified in the replication configuration, the\n replicas are owned by same Amazon Web Services account that owns the source object.

" + "smithy.api#documentation": "

Specify this only in a cross-account scenario (where source and destination bucket\n owners are not the same), and you want to change replica ownership to the Amazon Web Services account\n that owns the destination bucket. If this is not specified in the replication\n configuration, the replicas are owned by same Amazon Web Services account that owns the source\n object.

" } }, "EncryptionConfiguration": { @@ -22544,14 +24164,14 @@ "EncryptionType": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing job results in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing job results in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#required": {} } }, "KMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If the encryption type is aws:kms, this optional value specifies the ID of\n the symmetric customer managed key to use for encryption of job results. Amazon S3 only\n supports symmetric keys. For more information, see Using symmetric and\n asymmetric keys in the Amazon Web Services Key Management Service Developer\n Guide.

" + "smithy.api#documentation": "

If the encryption type is aws:kms, this optional value specifies the ID of\n the symmetric encryption customer managed key to use for encryption of job results. Amazon S3 only\n supports symmetric encryption KMS keys. For more information, see Asymmetric keys in KMS in the Amazon Web Services Key Management Service\n Developer Guide.

" } }, "KMSContext": { @@ -22571,7 +24191,7 @@ "ReplicaKmsKeyID": { "target": "com.amazonaws.s3#ReplicaKmsKeyID", "traits": { - "smithy.api#documentation": "

Specifies the ID (Key ARN or Alias ARN) of the customer managed Amazon Web Services KMS key\n stored in Amazon Web Services Key Management Service (KMS) for the destination bucket. Amazon S3 uses\n this key to encrypt replica objects. Amazon S3 only supports symmetric, customer managed KMS keys.\n For more information, see Using symmetric and\n asymmetric keys in the Amazon Web Services Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the ID (Key ARN or Alias ARN) of the customer managed Amazon Web Services KMS key stored in\n Amazon Web Services Key Management Service (KMS) for the destination bucket. Amazon S3 uses this key to\n encrypt replica objects. Amazon S3 only supports symmetric encryption KMS keys. For more\n information, see Asymmetric keys in Amazon Web Services\n KMS in the Amazon Web Services Key Management Service Developer\n Guide.

" } } }, @@ -22610,7 +24230,7 @@ "Code": { "target": "com.amazonaws.s3#Code", "traits": { - "smithy.api#documentation": "

The error code is a string that uniquely identifies an error condition. It is meant to\n be read and understood by programs that detect and handle errors by type.

\n

\n Amazon S3 error codes\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: AccessDenied

      \n
    • \n
    • \n

      \n Description: Access Denied

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AccountProblem

      \n
    • \n
    • \n

      \n Description: There is a problem with your Amazon Web Services account\n that prevents the action from completing successfully. Contact Amazon Web Services Support\n for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AllAccessDisabled

      \n
    • \n
    • \n

      \n Description: All access to this Amazon S3 resource has been\n disabled. Contact Amazon Web Services Support for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AmbiguousGrantByEmailAddress

      \n
    • \n
    • \n

      \n Description: The email address you provided is\n associated with more than one account.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AuthorizationHeaderMalformed

      \n
    • \n
    • \n

      \n Description: The authorization header you provided is\n invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BadDigest

      \n
    • \n
    • \n

      \n Description: The Content-MD5 you specified did not\n match what we received.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketAlreadyExists

      \n
    • \n
    • \n

      \n Description: The requested bucket name is not\n available. The bucket namespace is shared by all users of the system. Please\n select a different name and try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketAlreadyOwnedByYou

      \n
    • \n
    • \n

      \n Description: The bucket you tried to create already\n exists, and you own it. Amazon S3 returns this error in all Amazon Web Services Regions except in\n the North Virginia Region. For legacy compatibility, if you re-create an\n existing bucket that you already own in the North Virginia Region, Amazon S3 returns\n 200 OK and resets the bucket access control lists (ACLs).

      \n
    • \n
    • \n

      \n Code: 409 Conflict (in all Regions except the North\n Virginia Region)

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketNotEmpty

      \n
    • \n
    • \n

      \n Description: The bucket you tried to delete is not\n empty.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: CredentialsNotSupported

      \n
    • \n
    • \n

      \n Description: This request does not support\n credentials.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: CrossLocationLoggingProhibited

      \n
    • \n
    • \n

      \n Description: Cross-location logging not allowed.\n Buckets in one geographic location cannot log information to a bucket in\n another location.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: EntityTooSmall

      \n
    • \n
    • \n

      \n Description: Your proposed upload is smaller than the\n minimum allowed object size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: EntityTooLarge

      \n
    • \n
    • \n

      \n Description: Your proposed upload exceeds the maximum\n allowed object size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: ExpiredToken

      \n
    • \n
    • \n

      \n Description: The provided token has expired.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IllegalVersioningConfigurationException

      \n
    • \n
    • \n

      \n Description: Indicates that the versioning\n configuration specified in the request is invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IncompleteBody

      \n
    • \n
    • \n

      \n Description: You did not provide the number of bytes\n specified by the Content-Length HTTP header

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IncorrectNumberOfFilesInPostRequest

      \n
    • \n
    • \n

      \n Description: POST requires exactly one file upload per\n request.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InlineDataTooLarge

      \n
    • \n
    • \n

      \n Description: Inline data exceeds the maximum allowed\n size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError

      \n
    • \n
    • \n

      \n Description: We encountered an internal error. Please\n try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 500 Internal Server Error

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidAccessKeyId

      \n
    • \n
    • \n

      \n Description: The Amazon Web Services access key ID you provided does\n not exist in our records.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidAddressingHeader

      \n
    • \n
    • \n

      \n Description: You must specify the Anonymous\n role.

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidArgument

      \n
    • \n
    • \n

      \n Description: Invalid Argument

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidBucketName

      \n
    • \n
    • \n

      \n Description: The specified bucket is not valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidBucketState

      \n
    • \n
    • \n

      \n Description: The request is not valid with the current\n state of the bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidDigest

      \n
    • \n
    • \n

      \n Description: The Content-MD5 you specified is not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidEncryptionAlgorithmError

      \n
    • \n
    • \n

      \n Description: The encryption request you specified is\n not valid. The valid value is AES256.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidLocationConstraint

      \n
    • \n
    • \n

      \n Description: The specified location constraint is not\n valid. For more information about Regions, see How to Select a\n Region for Your Buckets.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidObjectState

      \n
    • \n
    • \n

      \n Description: The action is not valid for the current\n state of the object.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPart

      \n
    • \n
    • \n

      \n Description: One or more of the specified parts could\n not be found. The part might not have been uploaded, or the specified entity\n tag might not have matched the part's entity tag.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPartOrder

      \n
    • \n
    • \n

      \n Description: The list of parts was not in ascending\n order. Parts list must be specified in order by part number.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPayer

      \n
    • \n
    • \n

      \n Description: All access to this object has been\n disabled. Please contact Amazon Web Services Support for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPolicyDocument

      \n
    • \n
    • \n

      \n Description: The content of the form does not meet the\n conditions specified in the policy document.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRange

      \n
    • \n
    • \n

      \n Description: The requested range cannot be\n satisfied.

      \n
    • \n
    • \n

      \n HTTP Status Code: 416 Requested Range Not\n Satisfiable

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Please use AWS4-HMAC-SHA256.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: SOAP requests must be made over an HTTPS\n connection.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported for buckets with non-DNS compliant names.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported for buckets with periods (.) in their names.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate endpoint only\n supports virtual style requests.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate is not configured\n on this bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate is disabled on\n this bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported on this bucket. Contact Amazon Web Services Support for more information.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration cannot be\n enabled on this bucket. Contact Amazon Web Services Support for more information.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidSecurity

      \n
    • \n
    • \n

      \n Description: The provided security credentials are not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidSOAPRequest

      \n
    • \n
    • \n

      \n Description: The SOAP request body is invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidStorageClass

      \n
    • \n
    • \n

      \n Description: The storage class you specified is not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidTargetBucketForLogging

      \n
    • \n
    • \n

      \n Description: The target bucket for logging does not\n exist, is not owned by you, or does not have the appropriate grants for the\n log-delivery group.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidToken

      \n
    • \n
    • \n

      \n Description: The provided token is malformed or\n otherwise invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidURI

      \n
    • \n
    • \n

      \n Description: Couldn't parse the specified URI.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: KeyTooLongError

      \n
    • \n
    • \n

      \n Description: Your key is too long.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedACLError

      \n
    • \n
    • \n

      \n Description: The XML you provided was not well-formed\n or did not validate against our published schema.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedPOSTRequest

      \n
    • \n
    • \n

      \n Description: The body of your POST request is not\n well-formed multipart/form-data.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXML

      \n
    • \n
    • \n

      \n Description: This happens when the user sends malformed\n XML (XML that doesn't conform to the published XSD) for the configuration. The\n error message is, \"The XML you provided was not well-formed or did not validate\n against our published schema.\"

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MaxMessageLengthExceeded

      \n
    • \n
    • \n

      \n Description: Your request was too big.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MaxPostPreDataLengthExceededError

      \n
    • \n
    • \n

      \n Description: Your POST request fields preceding the\n upload file were too large.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MetadataTooLarge

      \n
    • \n
    • \n

      \n Description: Your metadata headers exceed the maximum\n allowed metadata size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MethodNotAllowed

      \n
    • \n
    • \n

      \n Description: The specified method is not allowed\n against this resource.

      \n
    • \n
    • \n

      \n HTTP Status Code: 405 Method Not Allowed

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingAttachment

      \n
    • \n
    • \n

      \n Description: A SOAP attachment was expected, but none\n were found.

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingContentLength

      \n
    • \n
    • \n

      \n Description: You must provide the Content-Length HTTP\n header.

      \n
    • \n
    • \n

      \n HTTP Status Code: 411 Length Required

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingRequestBodyError

      \n
    • \n
    • \n

      \n Description: This happens when the user sends an empty\n XML document as a request. The error message is, \"Request body is empty.\"\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingSecurityElement

      \n
    • \n
    • \n

      \n Description: The SOAP 1.1 request is missing a security\n element.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingSecurityHeader

      \n
    • \n
    • \n

      \n Description: Your request is missing a required\n header.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoLoggingStatusForKey

      \n
    • \n
    • \n

      \n Description: There is no such thing as a logging status\n subresource for a key.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchBucket

      \n
    • \n
    • \n

      \n Description: The specified bucket does not\n exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchBucketPolicy

      \n
    • \n
    • \n

      \n Description: The specified bucket does not have a\n bucket policy.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchKey

      \n
    • \n
    • \n

      \n Description: The specified key does not exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchLifecycleConfiguration

      \n
    • \n
    • \n

      \n Description: The lifecycle configuration does not\n exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload

      \n
    • \n
    • \n

      \n Description: The specified multipart upload does not\n exist. The upload ID might be invalid, or the multipart upload might have been\n aborted or completed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchVersion

      \n
    • \n
    • \n

      \n Description: Indicates that the version ID specified in\n the request does not match an existing version.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NotImplemented

      \n
    • \n
    • \n

      \n Description: A header you provided implies\n functionality that is not implemented.

      \n
    • \n
    • \n

      \n HTTP Status Code: 501 Not Implemented

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NotSignedUp

      \n
    • \n
    • \n

      \n Description: Your account is not signed up for the Amazon S3\n service. You must sign up before you can use Amazon S3. You can sign up at the\n following URL: Amazon S3\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAborted

      \n
    • \n
    • \n

      \n Description: A conflicting conditional action is\n currently in progress against this resource. Try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: PermanentRedirect

      \n
    • \n
    • \n

      \n Description: The bucket you are attempting to access\n must be addressed using the specified endpoint. Send all future requests to\n this endpoint.

      \n
    • \n
    • \n

      \n HTTP Status Code: 301 Moved Permanently

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: PreconditionFailed

      \n
    • \n
    • \n

      \n Description: At least one of the preconditions you\n specified did not hold.

      \n
    • \n
    • \n

      \n HTTP Status Code: 412 Precondition Failed

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: Redirect

      \n
    • \n
    • \n

      \n Description: Temporary redirect.

      \n
    • \n
    • \n

      \n HTTP Status Code: 307 Moved Temporarily

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress

      \n
    • \n
    • \n

      \n Description: Object restore is already in\n progress.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestIsNotMultiPartContent

      \n
    • \n
    • \n

      \n Description: Bucket POST must be of the enclosure-type\n multipart/form-data.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTimeout

      \n
    • \n
    • \n

      \n Description: Your socket connection to the server was\n not read from or written to within the timeout period.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTimeTooSkewed

      \n
    • \n
    • \n

      \n Description: The difference between the request time\n and the server's time is too large.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTorrentOfBucketError

      \n
    • \n
    • \n

      \n Description: Requesting the torrent file of a bucket is\n not permitted.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: SignatureDoesNotMatch

      \n
    • \n
    • \n

      \n Description: The request signature we calculated does\n not match the signature you provided. Check your Amazon Web Services secret access key and\n signing method. For more information, see REST Authentication and\n SOAP Authentication\n for details.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: ServiceUnavailable

      \n
    • \n
    • \n

      \n Description: Reduce your request rate.

      \n
    • \n
    • \n

      \n HTTP Status Code: 503 Service Unavailable

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: SlowDown

      \n
    • \n
    • \n

      \n Description: Reduce your request rate.

      \n
    • \n
    • \n

      \n HTTP Status Code: 503 Slow Down

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TemporaryRedirect

      \n
    • \n
    • \n

      \n Description: You are being redirected to the bucket\n while DNS updates.

      \n
    • \n
    • \n

      \n HTTP Status Code: 307 Moved Temporarily

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TokenRefreshRequired

      \n
    • \n
    • \n

      \n Description: The provided token must be\n refreshed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TooManyBuckets

      \n
    • \n
    • \n

      \n Description: You have attempted to create more buckets\n than allowed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UnexpectedContent

      \n
    • \n
    • \n

      \n Description: This request does not support\n content.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UnresolvableGrantByEmailAddress

      \n
    • \n
    • \n

      \n Description: The email address you provided does not\n match any account on record.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UserKeyMustBeSpecified

      \n
    • \n
    • \n

      \n Description: The bucket POST must contain the specified\n field name. If it is specified, check the order of the fields.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

" + "smithy.api#documentation": "

The error code is a string that uniquely identifies an error condition. It is meant to\n be read and understood by programs that detect and handle errors by type. The following is\n a list of Amazon S3 error codes. For more information, see Error responses.

\n
    \n
  • \n
      \n
    • \n

      \n Code: AccessDenied

      \n
    • \n
    • \n

      \n Description: Access Denied

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AccountProblem

      \n
    • \n
    • \n

      \n Description: There is a problem with your Amazon Web Services account\n that prevents the action from completing successfully. Contact Amazon Web Services Support\n for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AllAccessDisabled

      \n
    • \n
    • \n

      \n Description: All access to this Amazon S3 resource has been\n disabled. Contact Amazon Web Services Support for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AmbiguousGrantByEmailAddress

      \n
    • \n
    • \n

      \n Description: The email address you provided is\n associated with more than one account.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AuthorizationHeaderMalformed

      \n
    • \n
    • \n

      \n Description: The authorization header you provided is\n invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BadDigest

      \n
    • \n
    • \n

      \n Description: The Content-MD5 you specified did not\n match what we received.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketAlreadyExists

      \n
    • \n
    • \n

      \n Description: The requested bucket name is not\n available. The bucket namespace is shared by all users of the system. Please\n select a different name and try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketAlreadyOwnedByYou

      \n
    • \n
    • \n

      \n Description: The bucket you tried to create already\n exists, and you own it. Amazon S3 returns this error in all Amazon Web Services Regions except in\n the North Virginia Region. For legacy compatibility, if you re-create an\n existing bucket that you already own in the North Virginia Region, Amazon S3 returns\n 200 OK and resets the bucket access control lists (ACLs).

      \n
    • \n
    • \n

      \n Code: 409 Conflict (in all Regions except the North\n Virginia Region)

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketNotEmpty

      \n
    • \n
    • \n

      \n Description: The bucket you tried to delete is not\n empty.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: CredentialsNotSupported

      \n
    • \n
    • \n

      \n Description: This request does not support\n credentials.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: CrossLocationLoggingProhibited

      \n
    • \n
    • \n

      \n Description: Cross-location logging not allowed.\n Buckets in one geographic location cannot log information to a bucket in\n another location.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: EntityTooSmall

      \n
    • \n
    • \n

      \n Description: Your proposed upload is smaller than the\n minimum allowed object size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: EntityTooLarge

      \n
    • \n
    • \n

      \n Description: Your proposed upload exceeds the maximum\n allowed object size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: ExpiredToken

      \n
    • \n
    • \n

      \n Description: The provided token has expired.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IllegalVersioningConfigurationException

      \n
    • \n
    • \n

      \n Description: Indicates that the versioning\n configuration specified in the request is invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IncompleteBody

      \n
    • \n
    • \n

      \n Description: You did not provide the number of bytes\n specified by the Content-Length HTTP header

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IncorrectNumberOfFilesInPostRequest

      \n
    • \n
    • \n

      \n Description: POST requires exactly one file upload per\n request.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InlineDataTooLarge

      \n
    • \n
    • \n

      \n Description: Inline data exceeds the maximum allowed\n size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError

      \n
    • \n
    • \n

      \n Description: We encountered an internal error. Please\n try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 500 Internal Server Error

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidAccessKeyId

      \n
    • \n
    • \n

      \n Description: The Amazon Web Services access key ID you provided does\n not exist in our records.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidAddressingHeader

      \n
    • \n
    • \n

      \n Description: You must specify the Anonymous\n role.

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidArgument

      \n
    • \n
    • \n

      \n Description: Invalid Argument

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidBucketName

      \n
    • \n
    • \n

      \n Description: The specified bucket is not valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidBucketState

      \n
    • \n
    • \n

      \n Description: The request is not valid with the current\n state of the bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidDigest

      \n
    • \n
    • \n

      \n Description: The Content-MD5 you specified is not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidEncryptionAlgorithmError

      \n
    • \n
    • \n

      \n Description: The encryption request you specified is\n not valid. The valid value is AES256.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidLocationConstraint

      \n
    • \n
    • \n

      \n Description: The specified location constraint is not\n valid. For more information about Regions, see How to Select\n a Region for Your Buckets.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidObjectState

      \n
    • \n
    • \n

      \n Description: The action is not valid for the current\n state of the object.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPart

      \n
    • \n
    • \n

      \n Description: One or more of the specified parts could\n not be found. The part might not have been uploaded, or the specified entity\n tag might not have matched the part's entity tag.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPartOrder

      \n
    • \n
    • \n

      \n Description: The list of parts was not in ascending\n order. Parts list must be specified in order by part number.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPayer

      \n
    • \n
    • \n

      \n Description: All access to this object has been\n disabled. Please contact Amazon Web Services Support for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPolicyDocument

      \n
    • \n
    • \n

      \n Description: The content of the form does not meet the\n conditions specified in the policy document.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRange

      \n
    • \n
    • \n

      \n Description: The requested range cannot be\n satisfied.

      \n
    • \n
    • \n

      \n HTTP Status Code: 416 Requested Range Not\n Satisfiable

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Please use\n AWS4-HMAC-SHA256.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: SOAP requests must be made over an HTTPS\n connection.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported for buckets with non-DNS compliant names.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported for buckets with periods (.) in their names.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate endpoint only\n supports virtual style requests.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate is not configured\n on this bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate is disabled on\n this bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported on this bucket. Contact Amazon Web Services Support for more information.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration cannot be\n enabled on this bucket. Contact Amazon Web Services Support for more information.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidSecurity

      \n
    • \n
    • \n

      \n Description: The provided security credentials are not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidSOAPRequest

      \n
    • \n
    • \n

      \n Description: The SOAP request body is invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidStorageClass

      \n
    • \n
    • \n

      \n Description: The storage class you specified is not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidTargetBucketForLogging

      \n
    • \n
    • \n

      \n Description: The target bucket for logging does not\n exist, is not owned by you, or does not have the appropriate grants for the\n log-delivery group.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidToken

      \n
    • \n
    • \n

      \n Description: The provided token is malformed or\n otherwise invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidURI

      \n
    • \n
    • \n

      \n Description: Couldn't parse the specified URI.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: KeyTooLongError

      \n
    • \n
    • \n

      \n Description: Your key is too long.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedACLError

      \n
    • \n
    • \n

      \n Description: The XML you provided was not well-formed\n or did not validate against our published schema.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedPOSTRequest

      \n
    • \n
    • \n

      \n Description: The body of your POST request is not\n well-formed multipart/form-data.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXML

      \n
    • \n
    • \n

      \n Description: This happens when the user sends malformed\n XML (XML that doesn't conform to the published XSD) for the configuration. The\n error message is, \"The XML you provided was not well-formed or did not validate\n against our published schema.\"

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MaxMessageLengthExceeded

      \n
    • \n
    • \n

      \n Description: Your request was too big.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MaxPostPreDataLengthExceededError

      \n
    • \n
    • \n

      \n Description: Your POST request fields preceding the\n upload file were too large.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MetadataTooLarge

      \n
    • \n
    • \n

      \n Description: Your metadata headers exceed the maximum\n allowed metadata size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MethodNotAllowed

      \n
    • \n
    • \n

      \n Description: The specified method is not allowed\n against this resource.

      \n
    • \n
    • \n

      \n HTTP Status Code: 405 Method Not Allowed

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingAttachment

      \n
    • \n
    • \n

      \n Description: A SOAP attachment was expected, but none\n were found.

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingContentLength

      \n
    • \n
    • \n

      \n Description: You must provide the Content-Length HTTP\n header.

      \n
    • \n
    • \n

      \n HTTP Status Code: 411 Length Required

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingRequestBodyError

      \n
    • \n
    • \n

      \n Description: This happens when the user sends an empty\n XML document as a request. The error message is, \"Request body is empty.\"\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingSecurityElement

      \n
    • \n
    • \n

      \n Description: The SOAP 1.1 request is missing a security\n element.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingSecurityHeader

      \n
    • \n
    • \n

      \n Description: Your request is missing a required\n header.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoLoggingStatusForKey

      \n
    • \n
    • \n

      \n Description: There is no such thing as a logging status\n subresource for a key.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchBucket

      \n
    • \n
    • \n

      \n Description: The specified bucket does not\n exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchBucketPolicy

      \n
    • \n
    • \n

      \n Description: The specified bucket does not have a\n bucket policy.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchKey

      \n
    • \n
    • \n

      \n Description: The specified key does not exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchLifecycleConfiguration

      \n
    • \n
    • \n

      \n Description: The lifecycle configuration does not\n exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload

      \n
    • \n
    • \n

      \n Description: The specified multipart upload does not\n exist. The upload ID might be invalid, or the multipart upload might have been\n aborted or completed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchVersion

      \n
    • \n
    • \n

      \n Description: Indicates that the version ID specified in\n the request does not match an existing version.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NotImplemented

      \n
    • \n
    • \n

      \n Description: A header you provided implies\n functionality that is not implemented.

      \n
    • \n
    • \n

      \n HTTP Status Code: 501 Not Implemented

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NotSignedUp

      \n
    • \n
    • \n

      \n Description: Your account is not signed up for the Amazon S3\n service. You must sign up before you can use Amazon S3. You can sign up at the\n following URL: Amazon S3\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAborted

      \n
    • \n
    • \n

      \n Description: A conflicting conditional action is\n currently in progress against this resource. Try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: PermanentRedirect

      \n
    • \n
    • \n

      \n Description: The bucket you are attempting to access\n must be addressed using the specified endpoint. Send all future requests to\n this endpoint.

      \n
    • \n
    • \n

      \n HTTP Status Code: 301 Moved Permanently

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: PreconditionFailed

      \n
    • \n
    • \n

      \n Description: At least one of the preconditions you\n specified did not hold.

      \n
    • \n
    • \n

      \n HTTP Status Code: 412 Precondition Failed

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: Redirect

      \n
    • \n
    • \n

      \n Description: Temporary redirect.

      \n
    • \n
    • \n

      \n HTTP Status Code: 307 Moved Temporarily

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress

      \n
    • \n
    • \n

      \n Description: Object restore is already in\n progress.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestIsNotMultiPartContent

      \n
    • \n
    • \n

      \n Description: Bucket POST must be of the enclosure-type\n multipart/form-data.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTimeout

      \n
    • \n
    • \n

      \n Description: Your socket connection to the server was\n not read from or written to within the timeout period.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTimeTooSkewed

      \n
    • \n
    • \n

      \n Description: The difference between the request time\n and the server's time is too large.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTorrentOfBucketError

      \n
    • \n
    • \n

      \n Description: Requesting the torrent file of a bucket is\n not permitted.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: SignatureDoesNotMatch

      \n
    • \n
    • \n

      \n Description: The request signature we calculated does\n not match the signature you provided. Check your Amazon Web Services secret access key and\n signing method. For more information, see REST\n Authentication and SOAP\n Authentication for details.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: ServiceUnavailable

      \n
    • \n
    • \n

      \n Description: Service is unable to handle\n request.

      \n
    • \n
    • \n

      \n HTTP Status Code: 503 Service Unavailable

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: SlowDown

      \n
    • \n
    • \n

      \n Description: Reduce your request rate.

      \n
    • \n
    • \n

      \n HTTP Status Code: 503 Slow Down

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TemporaryRedirect

      \n
    • \n
    • \n

      \n Description: You are being redirected to the bucket\n while DNS updates.

      \n
    • \n
    • \n

      \n HTTP Status Code: 307 Moved Temporarily

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TokenRefreshRequired

      \n
    • \n
    • \n

      \n Description: The provided token must be\n refreshed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TooManyBuckets

      \n
    • \n
    • \n

      \n Description: You have attempted to create more buckets\n than allowed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UnexpectedContent

      \n
    • \n
    • \n

      \n Description: This request does not support\n content.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UnresolvableGrantByEmailAddress

      \n
    • \n
    • \n

      \n Description: The email address you provided does not\n match any account on record.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UserKeyMustBeSpecified

      \n
    • \n
    • \n

      \n Description: The bucket POST must contain the specified\n field name. If it is specified, check the order of the fields.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

" } }, "Message": { @@ -22652,92 +24272,173 @@ } }, "com.amazonaws.s3#Event": { - "type": "string", - "traits": { - "smithy.api#documentation": "

The bucket event for which to send notifications.

", - "smithy.api#enum": [ - { - "value": "s3:ReducedRedundancyLostObject" - }, - { - "value": "s3:ObjectCreated:*" - }, - { - "value": "s3:ObjectCreated:Put" - }, - { - "value": "s3:ObjectCreated:Post" - }, - { - "value": "s3:ObjectCreated:Copy" - }, - { - "value": "s3:ObjectCreated:CompleteMultipartUpload" - }, - { - "value": "s3:ObjectRemoved:*" - }, - { - "value": "s3:ObjectRemoved:Delete" - }, - { - "value": "s3:ObjectRemoved:DeleteMarkerCreated" - }, - { - "value": "s3:ObjectRestore:*" - }, - { - "value": "s3:ObjectRestore:Post" - }, - { - "value": "s3:ObjectRestore:Completed" - }, - { - "value": "s3:Replication:*" - }, - { - "value": "s3:Replication:OperationFailedReplication" - }, - { - "value": "s3:Replication:OperationNotTracked" - }, - { - "value": "s3:Replication:OperationMissedThreshold" - }, - { - "value": "s3:Replication:OperationReplicatedAfterThreshold" - }, - { - "value": "s3:ObjectRestore:Delete" - }, - { - "value": "s3:LifecycleTransition" - }, - { - "value": "s3:IntelligentTiering" - }, - { - "value": "s3:ObjectAcl:Put" - }, - { - "value": "s3:LifecycleExpiration:*" - }, - { - "value": "s3:LifecycleExpiration:Delete" - }, - { - "value": "s3:LifecycleExpiration:DeleteMarkerCreated" - }, - { - "value": "s3:ObjectTagging:*" - }, - { - "value": "s3:ObjectTagging:Put" - }, - { - "value": "s3:ObjectTagging:Delete" + "type": "enum", + "members": { + "s3_ReducedRedundancyLostObject": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ReducedRedundancyLostObject" + } + }, + "s3_ObjectCreated_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:*" + } + }, + "s3_ObjectCreated_Put": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:Put" + } + }, + "s3_ObjectCreated_Post": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:Post" + } + }, + "s3_ObjectCreated_Copy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:Copy" + } + }, + "s3_ObjectCreated_CompleteMultipartUpload": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:CompleteMultipartUpload" + } + }, + "s3_ObjectRemoved_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRemoved:*" + } + }, + "s3_ObjectRemoved_Delete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRemoved:Delete" + } + }, + "s3_ObjectRemoved_DeleteMarkerCreated": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRemoved:DeleteMarkerCreated" } - ] + }, + "s3_ObjectRestore_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRestore:*" + } + }, + "s3_ObjectRestore_Post": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRestore:Post" + } + }, + "s3_ObjectRestore_Completed": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRestore:Completed" + } + }, + "s3_Replication_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:*" + } + }, + "s3_Replication_OperationFailedReplication": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:OperationFailedReplication" + } + }, + "s3_Replication_OperationNotTracked": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:OperationNotTracked" + } + }, + "s3_Replication_OperationMissedThreshold": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:OperationMissedThreshold" + } + }, + "s3_Replication_OperationReplicatedAfterThreshold": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:OperationReplicatedAfterThreshold" + } + }, + "s3_ObjectRestore_Delete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRestore:Delete" + } + }, + "s3_LifecycleTransition": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:LifecycleTransition" + } + }, + "s3_IntelligentTiering": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:IntelligentTiering" + } + }, + "s3_ObjectAcl_Put": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectAcl:Put" + } + }, + "s3_LifecycleExpiration_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:LifecycleExpiration:*" + } + }, + "s3_LifecycleExpiration_Delete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:LifecycleExpiration:Delete" + } + }, + "s3_LifecycleExpiration_DeleteMarkerCreated": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:LifecycleExpiration:DeleteMarkerCreated" + } + }, + "s3_ObjectTagging_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectTagging:*" + } + }, + "s3_ObjectTagging_Put": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectTagging:Put" + } + }, + "s3_ObjectTagging_Delete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectTagging:Delete" + } + } + }, + "traits": { + "smithy.api#documentation": "

The bucket event for which to send notifications.

" } }, "com.amazonaws.s3#EventBridgeConfiguration": { @@ -22759,7 +24460,7 @@ "Status": { "target": "com.amazonaws.s3#ExistingObjectReplicationStatus", "traits": { - "smithy.api#documentation": "

", + "smithy.api#documentation": "

Specifies whether Amazon S3 replicates existing source bucket objects.

", "smithy.api#required": {} } } @@ -22927,7 +24628,7 @@ "target": "com.amazonaws.s3#GetBucketAccelerateConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

This implementation of the GET action uses the accelerate subresource to\n return the Transfer Acceleration state of a bucket, which is either Enabled or\n Suspended. Amazon S3 Transfer Acceleration is a bucket-level feature that\n enables you to perform faster data transfers to and from Amazon S3.

\n

To use this operation, you must have permission to perform the\n s3:GetAccelerateConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

You set the Transfer Acceleration state of an existing bucket to Enabled or\n Suspended by using the PutBucketAccelerateConfiguration operation.

\n

A GET accelerate request does not return a state value for a bucket that\n has no transfer acceleration state. A bucket has no Transfer Acceleration state if a state\n has never been set on the bucket.

\n

For more information about transfer acceleration, see Transfer Acceleration in the\n Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the GET action uses the accelerate subresource to\n return the Transfer Acceleration state of a bucket, which is either Enabled or\n Suspended. Amazon S3 Transfer Acceleration is a bucket-level feature that\n enables you to perform faster data transfers to and from Amazon S3.

\n

To use this operation, you must have permission to perform the\n s3:GetAccelerateConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

You set the Transfer Acceleration state of an existing bucket to Enabled or\n Suspended by using the PutBucketAccelerateConfiguration operation.

\n

A GET accelerate request does not return a state value for a bucket that\n has no transfer acceleration state. A bucket has no Transfer Acceleration state if a state\n has never been set on the bucket.

\n

For more information about transfer acceleration, see Transfer Acceleration in\n the Amazon S3 User Guide.

\n

The following operations are related to GetBucketAccelerateConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?accelerate", @@ -22943,6 +24644,12 @@ "traits": { "smithy.api#documentation": "

The accelerate configuration of the bucket.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -22970,6 +24677,12 @@ "smithy.api#documentation": "

The account ID of the expected bucket owner. If the bucket is owned by a different account, the request fails with the HTTP status code 403 Forbidden (access denied).

", "smithy.api#httpHeader": "x-amz-expected-bucket-owner" } + }, + "RequestPayer": { + "target": "com.amazonaws.s3#RequestPayer", + "traits": { + "smithy.api#httpHeader": "x-amz-request-payer" + } } }, "traits": { @@ -22985,7 +24698,7 @@ "target": "com.amazonaws.s3#GetBucketAclOutput" }, "traits": { - "smithy.api#documentation": "

This implementation of the GET action uses the acl\n subresource to return the access control list (ACL) of a bucket. To use GET to\n return the ACL of the bucket, you must have READ_ACP access to the bucket. If\n READ_ACP permission is granted to the anonymous user, you can return the\n ACL of the bucket without using an authorization header.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, \n requests to read ACLs are still supported and return the bucket-owner-full-control \n ACL with the owner being the account that created the bucket. For more information, see \n \n Controlling object ownership and disabling ACLs in the Amazon S3 User Guide.

\n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the GET action uses the acl subresource\n to return the access control list (ACL) of a bucket. To use GET to return the\n ACL of the bucket, you must have READ_ACP access to the bucket. If\n READ_ACP permission is granted to the anonymous user, you can return the\n ACL of the bucket without using an authorization header.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership,\n requests to read ACLs are still supported and return the\n bucket-owner-full-control ACL with the owner being the account that\n created the bucket. For more information, see Controlling object\n ownership and disabling ACLs in the\n Amazon S3 User Guide.

\n
\n

The following operations are related to GetBucketAcl:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?acl", @@ -23021,7 +24734,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the S3 bucket whose ACL is being requested.

", + "smithy.api#documentation": "

Specifies the S3 bucket whose ACL is being requested.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23050,7 +24763,7 @@ "target": "com.amazonaws.s3#GetBucketAnalyticsConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

This implementation of the GET action returns an analytics configuration (identified\n by the analytics configuration ID) from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:GetAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis in the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the GET action returns an analytics configuration (identified by\n the analytics configuration ID) from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:GetAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis in the Amazon S3 User Guide.

\n

The following operations are related to GetBucketAnalyticsConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?analytics&x-id=GetBucketAnalyticsConfiguration", @@ -23116,7 +24829,32 @@ "target": "com.amazonaws.s3#GetBucketCorsOutput" }, "traits": { - "smithy.api#documentation": "

Returns the Cross-Origin Resource Sharing (CORS) configuration information set for the\n bucket.

\n

To use this operation, you must have permission to perform the\n s3:GetBucketCORS action. By default, the bucket owner has this permission\n and can grant it to others.

\n

For more information about CORS, see Enabling Cross-Origin Resource\n Sharing.

\n

The following operations are related to GetBucketCors:

\n ", + "smithy.api#documentation": "

Returns the Cross-Origin Resource Sharing (CORS) configuration information set for the\n bucket.

\n

To use this operation, you must have permission to perform the\n s3:GetBucketCORS action. By default, the bucket owner has this permission\n and can grant it to others.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

For more information about CORS, see Enabling Cross-Origin Resource\n Sharing.

\n

The following operations are related to GetBucketCors:

\n ", + "smithy.api#examples": [ + { + "title": "To get cors configuration set on a bucket", + "documentation": "The following example returns cross-origin resource sharing (CORS) configuration set on a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "CORSRules": [ + { + "AllowedHeaders": [ + "Authorization" + ], + "MaxAgeSeconds": 3000, + "AllowedMethods": [ + "GET" + ], + "AllowedOrigins": [ + "*" + ] + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?cors", @@ -23147,7 +24885,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name for which to get the cors configuration.

", + "smithy.api#documentation": "

The bucket name for which to get the cors configuration.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23176,7 +24914,7 @@ "target": "com.amazonaws.s3#GetBucketEncryptionOutput" }, "traits": { - "smithy.api#documentation": "

Returns the default encryption configuration for an Amazon S3 bucket. If the bucket does not\n have a default encryption configuration, GetBucketEncryption returns\n ServerSideEncryptionConfigurationNotFoundError.

\n

For information about the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption.

\n

To use this operation, you must have permission to perform the\n s3:GetEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

The following operations are related to GetBucketEncryption:

\n ", + "smithy.api#documentation": "

Returns the default encryption configuration for an Amazon S3 bucket. By default, all buckets\n have a default encryption configuration that uses server-side encryption with Amazon S3 managed\n keys (SSE-S3). For information about the bucket default encryption feature, see Amazon S3 Bucket\n Default Encryption in the Amazon S3 User Guide.

\n

To use this operation, you must have permission to perform the\n s3:GetEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The following operations are related to GetBucketEncryption:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?encryption", @@ -23233,7 +24971,7 @@ "target": "com.amazonaws.s3#GetBucketIntelligentTieringConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

Gets the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to\n GetBucketIntelligentTieringConfiguration include:

\n ", + "smithy.api#documentation": "

Gets the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to GetBucketIntelligentTieringConfiguration include:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?intelligent-tiering&x-id=GetBucketIntelligentTieringConfiguration", @@ -23292,7 +25030,7 @@ "target": "com.amazonaws.s3#GetBucketInventoryConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

Returns an inventory configuration (identified by the inventory configuration ID) from\n the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:GetInventoryConfiguration action. The bucket owner has this permission\n by default and can grant this permission to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

\n

The following operations are related to\n GetBucketInventoryConfiguration:

\n ", + "smithy.api#documentation": "

Returns an inventory configuration (identified by the inventory configuration ID) from\n the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:GetInventoryConfiguration action. The bucket owner has this permission\n by default and can grant this permission to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

\n

The following operations are related to\n GetBucketInventoryConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?inventory&x-id=GetBucketInventoryConfiguration", @@ -23358,7 +25096,31 @@ "target": "com.amazonaws.s3#GetBucketLifecycleConfigurationOutput" }, "traits": { - "smithy.api#documentation": "\n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The response describes the new filter element\n that you can use to specify a filter to select a subset of objects to which the rule\n applies. If you are using a previous version of the lifecycle configuration, it still\n works. For the earlier action, see GetBucketLifecycle.

\n
\n

Returns the lifecycle configuration information set on the bucket. For information about\n lifecycle configuration, see Object\n Lifecycle Management.

\n

To use this operation, you must have permission to perform the\n s3:GetLifecycleConfiguration action. The bucket owner has this permission,\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

\n GetBucketLifecycleConfiguration has the following special error:

\n
    \n
  • \n

    Error code: NoSuchLifecycleConfiguration\n

    \n
      \n
    • \n

      Description: The lifecycle configuration does not exist.

      \n
    • \n
    • \n

      HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

The following operations are related to\n GetBucketLifecycleConfiguration:

\n ", + "smithy.api#documentation": "\n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The response describes the new filter element\n that you can use to specify a filter to select a subset of objects to which the rule\n applies. If you are using a previous version of the lifecycle configuration, it still\n works. For the earlier action, see GetBucketLifecycle.

\n
\n

Returns the lifecycle configuration information set on the bucket. For information about\n lifecycle configuration, see Object Lifecycle\n Management.

\n

To use this operation, you must have permission to perform the\n s3:GetLifecycleConfiguration action. The bucket owner has this permission,\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

\n GetBucketLifecycleConfiguration has the following special error:

\n
    \n
  • \n

    Error code: NoSuchLifecycleConfiguration\n

    \n
      \n
    • \n

      Description: The lifecycle configuration does not exist.

      \n
    • \n
    • \n

      HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

The following operations are related to\n GetBucketLifecycleConfiguration:

\n ", + "smithy.api#examples": [ + { + "title": "To get lifecycle configuration on a bucket", + "documentation": "The following example retrieves lifecycle configuration on set on a bucket. ", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Rules": [ + { + "Prefix": "TaxDocs", + "Status": "Enabled", + "Transitions": [ + { + "Days": 365, + "StorageClass": "STANDARD_IA" + } + ], + "ID": "Rule for TaxDocs/" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?lifecycle", @@ -23419,7 +25181,19 @@ }, "traits": { "aws.customizations#s3UnwrappedXmlOutput": {}, - "smithy.api#documentation": "

Returns the Region the bucket resides in. You set the bucket's Region using the\n LocationConstraint request parameter in a CreateBucket\n request. For more information, see CreateBucket.

\n

To use this implementation of the operation, you must be the bucket owner.

\n

To use this API against an access point, provide the alias of the access point in place of the bucket name.

\n

The following operations are related to GetBucketLocation:

\n ", + "smithy.api#documentation": "

Returns the Region the bucket resides in. You set the bucket's Region using the\n LocationConstraint request parameter in a CreateBucket\n request. For more information, see CreateBucket.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n \n

We recommend that you use HeadBucket to return the Region\n that a bucket resides in. For backward compatibility, Amazon S3 continues to support\n GetBucketLocation.

\n
\n

The following operations are related to GetBucketLocation:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket location", + "documentation": "The following example returns bucket location.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "LocationConstraint": "us-west-2" + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?location", @@ -23433,7 +25207,7 @@ "LocationConstraint": { "target": "com.amazonaws.s3#BucketLocationConstraint", "traits": { - "smithy.api#documentation": "

Specifies the Region where the bucket resides. For a list of all the Amazon S3 supported\n location constraints by Region, see Regions and Endpoints.\n Buckets in Region us-east-1 have a LocationConstraint of\n null.

" + "smithy.api#documentation": "

Specifies the Region where the bucket resides. For a list of all the Amazon S3 supported\n location constraints by Region, see Regions and Endpoints. Buckets in\n Region us-east-1 have a LocationConstraint of null.

" } } }, @@ -23448,7 +25222,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket for which to get the location.

", + "smithy.api#documentation": "

The name of the bucket for which to get the location.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23477,7 +25251,7 @@ "target": "com.amazonaws.s3#GetBucketLoggingOutput" }, "traits": { - "smithy.api#documentation": "

Returns the logging status of a bucket and the permissions users have to view and modify\n that status. To use GET, you must be the bucket owner.

\n

The following operations are related to GetBucketLogging:

\n ", + "smithy.api#documentation": "

Returns the logging status of a bucket and the permissions users have to view and modify\n that status.

\n

The following operations are related to GetBucketLogging:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?logging", @@ -23532,7 +25306,7 @@ "target": "com.amazonaws.s3#GetBucketMetricsConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

Gets a metrics configuration (specified by the metrics configuration ID) from the\n bucket. Note that this doesn't include the daily storage metrics.

\n

To use this operation, you must have permissions to perform the\n s3:GetMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon\n CloudWatch.

\n

The following operations are related to\n GetBucketMetricsConfiguration:

\n ", + "smithy.api#documentation": "

Gets a metrics configuration (specified by the metrics configuration ID) from the\n bucket. Note that this doesn't include the daily storage metrics.

\n

To use this operation, you must have permissions to perform the\n s3:GetMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring\n Metrics with Amazon CloudWatch.

\n

The following operations are related to\n GetBucketMetricsConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?metrics&x-id=GetBucketMetricsConfiguration", @@ -23572,7 +25346,7 @@ "Id": { "target": "com.amazonaws.s3#MetricsId", "traits": { - "smithy.api#documentation": "

The ID used to identify the metrics configuration.

", + "smithy.api#documentation": "

The ID used to identify the metrics configuration. The ID has a 64 character limit and\n can only contain letters, numbers, periods, dashes, and underscores.

", "smithy.api#httpQuery": "id", "smithy.api#required": {} } @@ -23598,7 +25372,7 @@ "target": "com.amazonaws.s3#NotificationConfiguration" }, "traits": { - "smithy.api#documentation": "

Returns the notification configuration of a bucket.

\n

If notifications are not enabled on the bucket, the action returns an empty\n NotificationConfiguration element.

\n

By default, you must be the bucket owner to read the notification configuration of a\n bucket. However, the bucket owner can use a bucket policy to grant permission to other\n users to read this configuration with the s3:GetBucketNotification\n permission.

\n

For more information about setting and reading the notification configuration on a\n bucket, see Setting Up Notification of\n Bucket Events. For more information about bucket policies, see Using Bucket Policies.

\n

The following action is related to GetBucketNotification:

\n ", + "smithy.api#documentation": "

Returns the notification configuration of a bucket.

\n

If notifications are not enabled on the bucket, the action returns an empty\n NotificationConfiguration element.

\n

By default, you must be the bucket owner to read the notification configuration of a\n bucket. However, the bucket owner can use a bucket policy to grant permission to other\n users to read this configuration with the s3:GetBucketNotification\n permission.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

For more information about setting and reading the notification configuration on a\n bucket, see Setting Up Notification of Bucket Events. For more information about bucket\n policies, see Using Bucket Policies.

\n

The following action is related to GetBucketNotification:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?notification", @@ -23612,7 +25386,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket for which to get the notification configuration.

", + "smithy.api#documentation": "

The name of the bucket for which to get the notification configuration.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23641,7 +25415,7 @@ "target": "com.amazonaws.s3#GetBucketOwnershipControlsOutput" }, "traits": { - "smithy.api#documentation": "

Retrieves OwnershipControls for an Amazon S3 bucket. To use this operation, you\n must have the s3:GetBucketOwnershipControls permission. For more information\n about Amazon S3 permissions, see Specifying\n permissions in a policy.

\n

For information about Amazon S3 Object Ownership, see Using Object Ownership.

\n

The following operations are related to GetBucketOwnershipControls:

\n ", + "smithy.api#documentation": "

Retrieves OwnershipControls for an Amazon S3 bucket. To use this operation, you\n must have the s3:GetBucketOwnershipControls permission. For more information\n about Amazon S3 permissions, see Specifying permissions in a\n policy.

\n

For information about Amazon S3 Object Ownership, see Using Object\n Ownership.

\n

The following operations are related to GetBucketOwnershipControls:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?ownershipControls", @@ -23655,7 +25429,7 @@ "OwnershipControls": { "target": "com.amazonaws.s3#OwnershipControls", "traits": { - "smithy.api#documentation": "

The OwnershipControls (BucketOwnerEnforced, BucketOwnerPreferred, or ObjectWriter) currently in\n effect for this Amazon S3 bucket.

", + "smithy.api#documentation": "

The OwnershipControls (BucketOwnerEnforced, BucketOwnerPreferred, or\n ObjectWriter) currently in effect for this Amazon S3 bucket.

", "smithy.api#httpPayload": {} } } @@ -23699,7 +25473,19 @@ "target": "com.amazonaws.s3#GetBucketPolicyOutput" }, "traits": { - "smithy.api#documentation": "

Returns the policy of a specified bucket. If you are using an identity other than the\n root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n GetBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have GetBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

As a security precaution, the root user of the Amazon Web Services account that owns a bucket can\n always use this operation, even if the policy explicitly denies the root user the\n ability to perform this action.

\n
\n

For more information about bucket policies, see Using Bucket Policies and User\n Policies.

\n

The following action is related to GetBucketPolicy:

\n ", + "smithy.api#documentation": "

Returns the policy of a specified bucket. If you are using an identity other than the\n root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n GetBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have GetBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

For more information about bucket policies, see Using Bucket Policies and User\n Policies.

\n

The following action is related to GetBucketPolicy:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket policy", + "documentation": "The following example returns bucket policy associated with a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Policy": "{\"Version\":\"2008-10-17\",\"Id\":\"LogPolicy\",\"Statement\":[{\"Sid\":\"Enables the log delivery group to publish logs to your bucket \",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"111122223333\"},\"Action\":[\"s3:GetBucketAcl\",\"s3:GetObjectAcl\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::policytest1/*\",\"arn:aws:s3:::policytest1\"]}]}" + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?policy", @@ -23728,7 +25514,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name for which to get the bucket policy.

", + "smithy.api#documentation": "

The bucket name for which to get the bucket policy.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23757,7 +25543,7 @@ "target": "com.amazonaws.s3#GetBucketPolicyStatusOutput" }, "traits": { - "smithy.api#documentation": "

Retrieves the policy status for an Amazon S3 bucket, indicating whether the bucket is public.\n In order to use this operation, you must have the s3:GetBucketPolicyStatus\n permission. For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n

For more information about when Amazon S3 considers a bucket public, see The Meaning of \"Public\".

\n

The following operations are related to GetBucketPolicyStatus:

\n ", + "smithy.api#documentation": "

Retrieves the policy status for an Amazon S3 bucket, indicating whether the bucket is public.\n In order to use this operation, you must have the s3:GetBucketPolicyStatus\n permission. For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n

For more information about when Amazon S3 considers a bucket public, see The Meaning of \"Public\".

\n

The following operations are related to GetBucketPolicyStatus:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?policyStatus", @@ -23816,6 +25602,30 @@ }, "traits": { "smithy.api#documentation": "

Returns the replication configuration of a bucket.

\n \n

It can take a while to propagate the put or delete a replication configuration to\n all Amazon S3 systems. Therefore, a get request soon after put or delete can return a wrong\n result.

\n
\n

For information about replication configuration, see Replication in the\n Amazon S3 User Guide.

\n

This action requires permissions for the s3:GetReplicationConfiguration\n action. For more information about permissions, see Using Bucket Policies and User\n Policies.

\n

If you include the Filter element in a replication configuration, you must\n also include the DeleteMarkerReplication and Priority elements.\n The response also returns those elements.

\n

For information about GetBucketReplication errors, see List of\n replication-related error codes\n

\n

The following operations are related to GetBucketReplication:

\n ", + "smithy.api#examples": [ + { + "title": "To get replication configuration set on a bucket", + "documentation": "The following example returns replication configuration set on a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "ReplicationConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Prefix": "Tax", + "Destination": { + "Bucket": "arn:aws:s3:::destination-bucket" + }, + "ID": "MWIwNTkwZmItMTE3MS00ZTc3LWJkZDEtNzRmODQwYzc1OTQy" + } + ], + "Role": "arn:aws:iam::acct-id:role/example-role" + } + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?replication", @@ -23872,7 +25682,19 @@ "target": "com.amazonaws.s3#GetBucketRequestPaymentOutput" }, "traits": { - "smithy.api#documentation": "

Returns the request payment configuration of a bucket. To use this version of the\n operation, you must be the bucket owner. For more information, see Requester Pays Buckets.

\n

The following operations are related to GetBucketRequestPayment:

\n ", + "smithy.api#documentation": "

Returns the request payment configuration of a bucket. To use this version of the\n operation, you must be the bucket owner. For more information, see Requester Pays\n Buckets.

\n

The following operations are related to GetBucketRequestPayment:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket versioning configuration", + "documentation": "The following example retrieves bucket versioning configuration.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Payer": "BucketOwner" + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?requestPayment", @@ -23931,6 +25753,27 @@ }, "traits": { "smithy.api#documentation": "

Returns the tag set associated with the bucket.

\n

To use this operation, you must have permission to perform the\n s3:GetBucketTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

\n GetBucketTagging has the following special error:

\n
    \n
  • \n

    Error code: NoSuchTagSet\n

    \n
      \n
    • \n

      Description: There is no tag set associated with the bucket.

      \n
    • \n
    \n
  • \n
\n

The following operations are related to GetBucketTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To get tag set associated with a bucket", + "documentation": "The following example returns tag set associated with a bucket", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "TagSet": [ + { + "Value": "value1", + "Key": "key1" + }, + { + "Value": "value2", + "Key": "key2" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?tagging", @@ -23990,6 +25833,19 @@ }, "traits": { "smithy.api#documentation": "

Returns the versioning state of a bucket.

\n

To retrieve the versioning state of a bucket, you must be the bucket owner.

\n

This implementation also returns the MFA Delete status of the versioning state. If the\n MFA Delete status is enabled, the bucket owner must use an authentication\n device to change the versioning state of the bucket.

\n

The following operations are related to GetBucketVersioning:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket versioning configuration", + "documentation": "The following example retrieves bucket versioning configuration.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Status": "Enabled", + "MFADelete": "Disabled" + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?versioning", @@ -24054,7 +25910,24 @@ "target": "com.amazonaws.s3#GetBucketWebsiteOutput" }, "traits": { - "smithy.api#documentation": "

Returns the website configuration for a bucket. To host website on Amazon S3, you can\n configure a bucket as website by adding a website configuration. For more information about\n hosting websites, see Hosting Websites on\n Amazon S3.

\n

This GET action requires the S3:GetBucketWebsite permission. By default,\n only the bucket owner can read the bucket website configuration. However, bucket owners can\n allow other users to read the website configuration by writing a bucket policy granting\n them the S3:GetBucketWebsite permission.

\n

The following operations are related to DeleteBucketWebsite:

\n ", + "smithy.api#documentation": "

Returns the website configuration for a bucket. To host website on Amazon S3, you can\n configure a bucket as website by adding a website configuration. For more information about\n hosting websites, see Hosting Websites on Amazon S3.

\n

This GET action requires the S3:GetBucketWebsite permission. By default,\n only the bucket owner can read the bucket website configuration. However, bucket owners can\n allow other users to read the website configuration by writing a bucket policy granting\n them the S3:GetBucketWebsite permission.

\n

The following operations are related to GetBucketWebsite:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket website configuration", + "documentation": "The following example retrieves website configuration of a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "IndexDocument": { + "Suffix": "index.html" + }, + "ErrorDocument": { + "Key": "error.html" + } + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?website", @@ -24147,7 +26020,7 @@ "SHA1" ] }, - "smithy.api#documentation": "

Retrieves objects from Amazon S3. To use GET, you must have READ\n access to the object. If you grant READ access to the anonymous user, you can\n return the object without using an authorization header.

\n

An Amazon S3 bucket has no directory hierarchy such as you would find in a typical computer\n file system. You can, however, create a logical hierarchy by using object key names that\n imply a folder structure. For example, instead of naming an object sample.jpg,\n you can name it photos/2006/February/sample.jpg.

\n

To get an object from such a logical hierarchy, specify the full key name for the object\n in the GET operation. For a virtual hosted-style request example, if you have\n the object photos/2006/February/sample.jpg, specify the resource as\n /photos/2006/February/sample.jpg. For a path-style request example, if you\n have the object photos/2006/February/sample.jpg in the bucket named\n examplebucket, specify the resource as\n /examplebucket/photos/2006/February/sample.jpg. For more information about\n request types, see HTTP Host Header Bucket Specification.

\n

For more information about returning the ACL of an object, see GetObjectAcl.

\n

If the object you are retrieving is stored in the S3 Glacier or\n S3 Glacier Deep Archive storage class, or S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, before you can retrieve the object you must first restore a\n copy using RestoreObject. Otherwise, this action returns an\n InvalidObjectStateError error. For information about restoring archived\n objects, see Restoring Archived\n Objects.

\n

Encryption request headers, like x-amz-server-side-encryption, should not\n be sent for GET requests if your object uses server-side encryption with KMS keys (SSE-KMS) \n or server-side encryption with Amazon S3–managed encryption keys (SSE-S3). If your\n object does use these types of keys, you’ll get an HTTP 400 BadRequest error.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you GET the object,\n you must use the following headers:

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption (Using\n Customer-Provided Encryption Keys).

\n

Assuming you have the relevant permission to read object tags, the response also returns the\n x-amz-tagging-count header that provides the count of number of tags\n associated with the object. You can use GetObjectTagging to retrieve\n the tag set associated with an object.

\n

\n Permissions\n

\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Specifying Permissions\n in a Policy. If the object you request does not exist, the error Amazon S3 returns\n depends on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 will\n return an HTTP status code 404 (\"no such key\") error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 will return an\n HTTP status code 403 (\"access denied\") error.

    \n
  • \n
\n

\n Versioning\n

\n

By default, the GET action returns the current version of an object. To return a\n different version, use the versionId subresource.

\n \n
    \n
  • \n

    \n If you supply a versionId, you need the s3:GetObjectVersion permission to\n access a specific version of an object. If you request a specific version, you do not need to have\n the s3:GetObject permission.\n

    \n
  • \n
  • \n

    If the current version of the object is a delete marker, Amazon S3 behaves as if the\n object was deleted and includes x-amz-delete-marker: true in the\n response.

    \n
  • \n
\n
\n

For more information about versioning, see PutBucketVersioning.

\n

\n Overriding Response Header Values\n

\n

There are times when you want to override certain response header values in a GET\n response. For example, you might override the Content-Disposition response\n header value in your GET request.

\n

You can override values for a set of response headers using the following query\n parameters. These response header values are sent only on a successful request, that is,\n when status code 200 OK is returned. The set of headers you can override using these\n parameters is a subset of the headers that Amazon S3 accepts when you create an object. The\n response headers that you can override for the GET response are Content-Type,\n Content-Language, Expires, Cache-Control,\n Content-Disposition, and Content-Encoding. To override these\n header values in the GET response, you use the following request parameters.

\n \n

You must sign the request, either using an Authorization header or a presigned URL,\n when using these parameters. They cannot be used with an unsigned (anonymous)\n request.

\n
\n
    \n
  • \n

    \n response-content-type\n

    \n
  • \n
  • \n

    \n response-content-language\n

    \n
  • \n
  • \n

    \n response-expires\n

    \n
  • \n
  • \n

    \n response-cache-control\n

    \n
  • \n
  • \n

    \n response-content-disposition\n

    \n
  • \n
  • \n

    \n response-content-encoding\n

    \n
  • \n
\n

\n Additional Considerations about Request Headers\n

\n

If both of the If-Match and If-Unmodified-Since headers are\n present in the request as follows: If-Match condition evaluates to\n true, and; If-Unmodified-Since condition evaluates to\n false; then, S3 returns 200 OK and the data requested.

\n

If both of the If-None-Match and If-Modified-Since headers are\n present in the request as follows: If-None-Match condition evaluates to\n false, and; If-Modified-Since condition evaluates to\n true; then, S3 returns 304 Not Modified response code.

\n

For more information about conditional requests, see RFC 7232.

\n

The following operations are related to GetObject:

\n ", + "smithy.api#documentation": "

Retrieves objects from Amazon S3. To use GET, you must have READ\n access to the object. If you grant READ access to the anonymous user, you can\n return the object without using an authorization header.

\n

An Amazon S3 bucket has no directory hierarchy such as you would find in a typical computer\n file system. You can, however, create a logical hierarchy by using object key names that\n imply a folder structure. For example, instead of naming an object sample.jpg,\n you can name it photos/2006/February/sample.jpg.

\n

To get an object from such a logical hierarchy, specify the full key name for the object\n in the GET operation. For a virtual hosted-style request example, if you have\n the object photos/2006/February/sample.jpg, specify the resource as\n /photos/2006/February/sample.jpg. For a path-style request example, if you\n have the object photos/2006/February/sample.jpg in the bucket named\n examplebucket, specify the resource as\n /examplebucket/photos/2006/February/sample.jpg. For more information about\n request types, see HTTP Host\n Header Bucket Specification.

\n

For more information about returning the ACL of an object, see GetObjectAcl.

\n

If the object you are retrieving is stored in the S3 Glacier Flexible Retrieval or\n S3 Glacier Deep Archive storage class, or S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, before you can retrieve the object you must first restore a\n copy using RestoreObject. Otherwise, this action returns an\n InvalidObjectState error. For information about restoring archived objects,\n see Restoring\n Archived Objects.

\n

Encryption request headers, like x-amz-server-side-encryption, should not\n be sent for GET requests if your object uses server-side encryption with Key Management Service (KMS)\n keys (SSE-KMS), dual-layer server-side encryption with Amazon Web Services KMS keys (DSSE-KMS), or\n server-side encryption with Amazon S3 managed encryption keys (SSE-S3). If your object does use\n these types of keys, you’ll get an HTTP 400 Bad Request error.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you GET the object,\n you must use the following headers:

\n
    \n
  • \n

    \n x-amz-server-side-encryption-customer-algorithm\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key-MD5\n

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys).

\n

Assuming you have the relevant permission to read object tags, the response also returns\n the x-amz-tagging-count header that provides the count of number of tags\n associated with the object. You can use GetObjectTagging to retrieve\n the tag set associated with an object.

\n
\n
Permissions
\n
\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Specifying Permissions in a\n Policy. If the object that you request doesn’t exist, the error that Amazon S3 returns depends\n on whether you also have the s3:ListBucket permission.

\n

If you have the s3:ListBucket permission on the bucket, Amazon S3\n returns an HTTP status code 404 (Not Found) error.

\n

If you don’t have the s3:ListBucket permission, Amazon S3 returns an\n HTTP status code 403 (\"access denied\") error.

\n
\n
Versioning
\n
\n

By default, the GET action returns the current version of an object. To return a\n different version, use the versionId subresource.

\n \n
    \n
  • \n

    If you supply a versionId, you need the\n s3:GetObjectVersion permission to access a specific version of an\n object. If you request a specific version, you do not need to have the\n s3:GetObject permission. If you request the current version\n without a specific version ID, only s3:GetObject permission is\n required. s3:GetObjectVersion permission won't be required.

    \n
  • \n
  • \n

    If the current version of the object is a delete marker, Amazon S3 behaves as if the\n object was deleted and includes x-amz-delete-marker: true in the\n response.

    \n
  • \n
\n
\n

For more information about versioning, see PutBucketVersioning.

\n
\n
Overriding Response Header Values
\n
\n

There are times when you want to override certain response header values in a GET\n response. For example, you might override the Content-Disposition response\n header value in your GET request.

\n

You can override values for a set of response headers using the following query\n parameters. These response header values are sent only on a successful request, that is,\n when status code 200 OK is returned. The set of headers you can override using these\n parameters is a subset of the headers that Amazon S3 accepts when you create an object. The\n response headers that you can override for the GET response are Content-Type,\n Content-Language, Expires, Cache-Control,\n Content-Disposition, and Content-Encoding. To override these\n header values in the GET response, you use the following request parameters.

\n \n

You must sign the request, either using an Authorization header or a presigned URL,\n when using these parameters. They cannot be used with an unsigned (anonymous)\n request.

\n
\n
    \n
  • \n

    \n response-content-type\n

    \n
  • \n
  • \n

    \n response-content-language\n

    \n
  • \n
  • \n

    \n response-expires\n

    \n
  • \n
  • \n

    \n response-cache-control\n

    \n
  • \n
  • \n

    \n response-content-disposition\n

    \n
  • \n
  • \n

    \n response-content-encoding\n

    \n
  • \n
\n
\n
Overriding Response Header Values
\n
\n

If both of the If-Match and If-Unmodified-Since headers are\n present in the request as follows: If-Match condition evaluates to\n true, and; If-Unmodified-Since condition evaluates to\n false; then, S3 returns 200 OK and the data requested.

\n

If both of the If-None-Match and If-Modified-Since headers are\n present in the request as follows: If-None-Match condition evaluates to\n false, and; If-Modified-Since condition evaluates to\n true; then, S3 returns 304 Not Modified response code.

\n

For more information about conditional requests, see RFC 7232.

\n
\n
\n

The following operations are related to GetObject:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?x-id=GetObject", @@ -24169,7 +26042,57 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the access control list (ACL) of an object. To use this operation, you must have\n s3:GetObjectAcl permissions or READ_ACP access to the object.\n For more information, see Mapping of ACL permissions and access policy permissions in the Amazon S3\n User Guide\n

\n

This action is not supported by Amazon S3 on Outposts.

\n

\n Versioning\n

\n

By default, GET returns ACL information about the current version of an object. To\n return ACL information about a different version, use the versionId subresource.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, \n requests to read ACLs are still supported and return the bucket-owner-full-control \n ACL with the owner being the account that created the bucket. For more information, see \n \n Controlling object ownership and disabling ACLs in the Amazon S3 User Guide.

\n
\n

The following operations are related to GetObjectAcl:

\n ", + "smithy.api#documentation": "

Returns the access control list (ACL) of an object. To use this operation, you must have\n s3:GetObjectAcl permissions or READ_ACP access to the object.\n For more information, see Mapping of ACL permissions and access policy permissions in the Amazon S3\n User Guide\n

\n

This action is not supported by Amazon S3 on Outposts.

\n

By default, GET returns ACL information about the current version of an object. To\n return ACL information about a different version, use the versionId subresource.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership,\n requests to read ACLs are still supported and return the\n bucket-owner-full-control ACL with the owner being the account that\n created the bucket. For more information, see Controlling object\n ownership and disabling ACLs in the\n Amazon S3 User Guide.

\n
\n

The following operations are related to GetObjectAcl:

\n ", + "smithy.api#examples": [ + { + "title": "To retrieve object ACL", + "documentation": "The following example retrieves access control list (ACL) of an object.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Grants": [ + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Permission": "WRITE" + }, + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Permission": "WRITE_ACP" + }, + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Permission": "READ" + }, + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "owner-display-name", + "ID": "852b113eexamplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Permission": "READ_ACP" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?acl", @@ -24266,7 +26189,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves all the metadata from an object without returning the object itself. This\n action is useful if you're interested only in an object's metadata. To use\n GetObjectAttributes, you must have READ access to the object.

\n

\n GetObjectAttributes combines the functionality of\n GetObjectAcl, GetObjectLegalHold,\n GetObjectLockConfiguration, GetObjectRetention,\n GetObjectTagging, HeadObject, and ListParts. All\n of the data returned with each of those individual calls can be returned with a single call\n to GetObjectAttributes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    \n x-amz-server-side-encryption-customer-algorithm\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key-MD5\n

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

\n \n
    \n
  • \n

    Encryption request headers, such as\n x-amz-server-side-encryption, should not be sent for GET requests\n if your object uses server-side encryption with Amazon Web Services KMS keys stored in Amazon Web Services Key\n Management Service (SSE-KMS) or server-side encryption with Amazon S3 managed\n encryption keys (SSE-S3). If your object does use these types of keys, you'll get\n an HTTP 400 Bad Request error.

    \n
  • \n
  • \n

    \n The last modified property in this case is the creation date of the object.

    \n
  • \n
\n
\n

Consider the following when using request headers:

\n
    \n
  • \n

    If both of the If-Match and If-Unmodified-Since\n headers are present in the request as follows, then Amazon S3 returns the HTTP\n status code 200 OK and the data requested:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true.

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false.

      \n
    • \n
    \n
  • \n
  • \n

    If both of the If-None-Match and If-Modified-Since\n headers are present in the request as follows, then Amazon S3 returns the HTTP status code\n 304 Not Modified:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to\n false.

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true.

      \n
    • \n
    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n

\n Permissions\n

\n

The permissions that you need to use this operation depend on whether the bucket is\n versioned. If the bucket is versioned, you need both the s3:GetObjectVersion\n and s3:GetObjectVersionAttributes permissions for this operation. If the\n bucket is not versioned, you need the s3:GetObject and\n s3:GetObjectAttributes permissions. For more information, see Specifying\n Permissions in a Policy in the Amazon S3 User Guide. If the\n object that you request does not exist, the error Amazon S3 returns depends on whether you also\n have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3\n returns an HTTP status code 404 Not Found (\"no such key\") error.

    \n
  • \n
  • \n

    If you don't have the s3:ListBucket permission, Amazon S3 returns an\n HTTP status code 403 Forbidden (\"access denied\") error.

    \n
  • \n
\n

The following actions are related to GetObjectAttributes:

\n ", + "smithy.api#documentation": "

Retrieves all the metadata from an object without returning the object itself. This\n action is useful if you're interested only in an object's metadata. To use\n GetObjectAttributes, you must have READ access to the object.

\n

\n GetObjectAttributes combines the functionality of HeadObject\n and ListParts. All of the data returned with each of those individual calls\n can be returned with a single call to GetObjectAttributes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    \n x-amz-server-side-encryption-customer-algorithm\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key-MD5\n

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

\n \n
    \n
  • \n

    Encryption request headers, such as x-amz-server-side-encryption,\n should not be sent for GET requests if your object uses server-side encryption\n with Amazon Web Services KMS keys stored in Amazon Web Services Key Management Service (SSE-KMS) or\n server-side encryption with Amazon S3 managed keys (SSE-S3). If your object does use\n these types of keys, you'll get an HTTP 400 Bad Request error.

    \n
  • \n
  • \n

    The last modified property in this case is the creation date of the\n object.

    \n
  • \n
\n
\n

Consider the following when using request headers:

\n
    \n
  • \n

    If both of the If-Match and If-Unmodified-Since headers\n are present in the request as follows, then Amazon S3 returns the HTTP status code\n 200 OK and the data requested:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true.

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false.

      \n
    • \n
    \n
  • \n
  • \n

    If both of the If-None-Match and If-Modified-Since\n headers are present in the request as follows, then Amazon S3 returns the HTTP status code\n 304 Not Modified:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to false.

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true.

      \n
    • \n
    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n
\n
Permissions
\n
\n

The permissions that you need to use this operation depend on whether the bucket is\n versioned. If the bucket is versioned, you need both the s3:GetObjectVersion\n and s3:GetObjectVersionAttributes permissions for this operation. If the\n bucket is not versioned, you need the s3:GetObject and\n s3:GetObjectAttributes permissions. For more information, see Specifying\n Permissions in a Policy in the Amazon S3 User Guide. If the\n object that you request does not exist, the error Amazon S3 returns depends on whether you also\n have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 returns\n an HTTP status code 404 Not Found (\"no such key\") error.

    \n
  • \n
  • \n

    If you don't have the s3:ListBucket permission, Amazon S3 returns an HTTP\n status code 403 Forbidden (\"access denied\") error.

    \n
  • \n
\n
\n
\n

The following actions are related to GetObjectAttributes:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?attributes", @@ -24308,7 +26231,7 @@ "ETag": { "target": "com.amazonaws.s3#ETag", "traits": { - "smithy.api#documentation": "

An ETag is an opaque identifier assigned by a web server to a specific version of a\n resource found at a URL.

" + "smithy.api#documentation": "

An ETag is an opaque identifier assigned by a web server to a specific version of a\n resource found at a URL.

" } }, "Checksum": { @@ -24326,7 +26249,7 @@ "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

Provides the storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage\n Classes.

" + "smithy.api#documentation": "

Provides the storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage Classes.

" } }, "ObjectSize": { @@ -24375,7 +26298,7 @@ "target": "com.amazonaws.s3#IsTruncated", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the returned list of parts is truncated. A value of\n true indicates that the list was truncated. A list can be truncated if the\n number of parts exceeds the limit returned in the MaxParts element.

" + "smithy.api#documentation": "

Indicates whether the returned list of parts is truncated. A value of true\n indicates that the list was truncated. A list can be truncated if the number of parts\n exceeds the limit returned in the MaxParts element.

" } }, "Parts": { @@ -24397,7 +26320,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket that contains the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket that contains the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -24431,28 +26354,28 @@ "PartNumberMarker": { "target": "com.amazonaws.s3#PartNumberMarker", "traits": { - "smithy.api#documentation": "

Specifies the part after which listing should begin. Only parts with higher part numbers\n will be listed.

", + "smithy.api#documentation": "

Specifies the part after which listing should begin. Only parts with higher part numbers\n will be listed.

", "smithy.api#httpHeader": "x-amz-part-number-marker" } }, "SSECustomerAlgorithm": { "target": "com.amazonaws.s3#SSECustomerAlgorithm", "traits": { - "smithy.api#documentation": "

Specifies the algorithm to use when encrypting the object (for example,\n AES256).

", + "smithy.api#documentation": "

Specifies the algorithm to use when encrypting the object (for example, AES256).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-customer-algorithm" } }, "SSECustomerKey": { "target": "com.amazonaws.s3#SSECustomerKey", "traits": { - "smithy.api#documentation": "

Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This\n value is used to store the object and then it is discarded; Amazon S3 does not store the\n encryption key. The key must be appropriate for use with the algorithm specified in the\n x-amz-server-side-encryption-customer-algorithm header.

", + "smithy.api#documentation": "

Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This\n value is used to store the object and then it is discarded; Amazon S3 does not store the\n encryption key. The key must be appropriate for use with the algorithm specified in the\n x-amz-server-side-encryption-customer-algorithm header.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-customer-key" } }, "SSECustomerKeyMD5": { "target": "com.amazonaws.s3#SSECustomerKeyMD5", "traits": { - "smithy.api#documentation": "

Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses\n this header for a message integrity check to ensure that the encryption key was transmitted\n without error.

", + "smithy.api#documentation": "

Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses\n this header for a message integrity check to ensure that the encryption key was transmitted\n without error.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-customer-key-MD5" } }, @@ -24472,7 +26395,7 @@ "ObjectAttributes": { "target": "com.amazonaws.s3#ObjectAttributesList", "traits": { - "smithy.api#documentation": "

An XML header that specifies the fields at the root level that you want returned in\n the response. Fields that you do not specify are not returned.

", + "smithy.api#documentation": "

An XML header that specifies the fields at the root level that you want returned in the\n response. Fields that you do not specify are not returned.

", "smithy.api#httpHeader": "x-amz-object-attributes", "smithy.api#required": {} } @@ -24570,7 +26493,7 @@ "target": "com.amazonaws.s3#GetObjectLockConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

Gets the Object Lock configuration for a bucket. The rule specified in the Object Lock\n configuration will be applied by default to every new object placed in the specified\n bucket. For more information, see Locking\n Objects.

\n

The following action is related to GetObjectLockConfiguration:

\n ", + "smithy.api#documentation": "

Gets the Object Lock configuration for a bucket. The rule specified in the Object Lock\n configuration will be applied by default to every new object placed in the specified\n bucket. For more information, see Locking Objects.

\n

The following action is related to GetObjectLockConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?object-lock", @@ -24655,7 +26578,7 @@ "Restore": { "target": "com.amazonaws.s3#Restore", "traits": { - "smithy.api#documentation": "

Provides information about object restoration action and expiration time of the\n restored object copy.

", + "smithy.api#documentation": "

Provides information about object restoration action and expiration time of the restored\n object copy.

", "smithy.api#httpHeader": "x-amz-restore" } }, @@ -24783,7 +26706,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -24811,7 +26734,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -24819,7 +26742,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with\n Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -24847,7 +26770,7 @@ "target": "com.amazonaws.s3#PartsCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The count of parts this object has. This value is only returned if you specify partNumber\n in your request and the object was uploaded as a multipart upload.

", + "smithy.api#documentation": "

The count of parts this object has. This value is only returned if you specify\n partNumber in your request and the object was uploaded as a multipart\n upload.

", "smithy.api#httpHeader": "x-amz-mp-parts-count" } }, @@ -24891,7 +26814,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using an Object Lambda access point the hostname takes the form AccessPointName-AccountId.s3-object-lambda.Region.amazonaws.com.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using an Object Lambda access point the hostname takes the form AccessPointName-AccountId.s3-object-lambda.Region.amazonaws.com.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -24938,7 +26861,7 @@ "Range": { "target": "com.amazonaws.s3#Range", "traits": { - "smithy.api#documentation": "

Downloads the specified range bytes of an object. For more information about the HTTP\n Range header, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.

\n \n

Amazon S3 doesn't support retrieving multiple ranges of data per GET\n request.

\n
", + "smithy.api#documentation": "

Downloads the specified range bytes of an object. For more information about the HTTP\n Range header, see https://www.rfc-editor.org/rfc/rfc9110.html#name-range.

\n \n

Amazon S3 doesn't support retrieving multiple ranges of data per GET\n request.

\n
", "smithy.api#httpHeader": "Range" } }, @@ -25001,7 +26924,7 @@ "SSECustomerKey": { "target": "com.amazonaws.s3#SSECustomerKey", "traits": { - "smithy.api#documentation": "

Specifies the customer-provided encryption key for Amazon S3 used to encrypt the data. This\n value is used to decrypt the object when recovering it and must match the one used when \n storing the data. The key must be appropriate for use with the algorithm specified in the\n x-amz-server-side-encryption-customer-algorithm header.

", + "smithy.api#documentation": "

Specifies the customer-provided encryption key for Amazon S3 used to encrypt the data. This\n value is used to decrypt the object when recovering it and must match the one used when\n storing the data. The key must be appropriate for use with the algorithm specified in the\n x-amz-server-side-encryption-customer-algorithm header.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-customer-key" } }, @@ -25060,7 +26983,7 @@ "target": "com.amazonaws.s3#GetObjectRetentionOutput" }, "traits": { - "smithy.api#documentation": "

Retrieves an object's retention settings. For more information, see Locking Objects.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectRetention:

\n ", + "smithy.api#documentation": "

Retrieves an object's retention settings. For more information, see Locking\n Objects.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectRetention:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?retention", @@ -25139,7 +27062,30 @@ "target": "com.amazonaws.s3#GetObjectTaggingOutput" }, "traits": { - "smithy.api#documentation": "

Returns the tag-set of an object. You send the GET request against the tagging\n subresource associated with the object.

\n

To use this operation, you must have permission to perform the\n s3:GetObjectTagging action. By default, the GET action returns\n information about current version of an object. For a versioned bucket, you can have\n multiple versions of an object in your bucket. To retrieve tags of any other version, use\n the versionId query parameter. You also need permission for the\n s3:GetObjectVersionTagging action.

\n

By default, the bucket owner has this permission and can grant this permission to\n others.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

The following actions are related to GetObjectTagging:

\n ", + "smithy.api#documentation": "

Returns the tag-set of an object. You send the GET request against the tagging\n subresource associated with the object.

\n

To use this operation, you must have permission to perform the\n s3:GetObjectTagging action. By default, the GET action returns information\n about current version of an object. For a versioned bucket, you can have multiple versions\n of an object in your bucket. To retrieve tags of any other version, use the versionId query\n parameter. You also need permission for the s3:GetObjectVersionTagging\n action.

\n

By default, the bucket owner has this permission and can grant this permission to\n others.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

The following actions are related to GetObjectTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To retrieve tag set of an object", + "documentation": "The following example retrieves tag set of an object.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "VersionId": "null", + "TagSet": [ + { + "Value": "Value4", + "Key": "Key4" + }, + { + "Value": "Value3", + "Key": "Key3" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?tagging", @@ -25176,7 +27122,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the object for which to get the tagging information.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the object for which to get the tagging information.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -25226,7 +27172,18 @@ "target": "com.amazonaws.s3#GetObjectTorrentOutput" }, "traits": { - "smithy.api#documentation": "

Returns torrent files from a bucket. BitTorrent can save you bandwidth when you're\n distributing large files. For more information about BitTorrent, see Using BitTorrent with Amazon S3.

\n \n

You can get torrent only for objects that are less than 5 GB in size, and that are\n not encrypted using server-side encryption with a customer-provided encryption\n key.

\n
\n

To use GET, you must have READ access to the object.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectTorrent:

\n ", + "smithy.api#documentation": "

Returns torrent files from a bucket. BitTorrent can save you bandwidth when you're\n distributing large files.

\n \n

You can get torrent only for objects that are less than 5 GB in size, and that are\n not encrypted using server-side encryption with a customer-provided encryption\n key.

\n
\n

To use GET, you must have READ access to the object.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectTorrent:

\n ", + "smithy.api#examples": [ + { + "title": "To retrieve torrent files for an object", + "documentation": "The following example retrieves torrent files of an object.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": {} + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?torrent", @@ -25305,7 +27262,7 @@ "target": "com.amazonaws.s3#GetPublicAccessBlockOutput" }, "traits": { - "smithy.api#documentation": "

Retrieves the PublicAccessBlock configuration for an Amazon S3 bucket. To use\n this operation, you must have the s3:GetBucketPublicAccessBlock permission.\n For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n \n

When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or\n an object, it checks the PublicAccessBlock configuration for both the\n bucket (or the bucket that contains the object) and the bucket owner's account. If the\n PublicAccessBlock settings are different between the bucket and the\n account, Amazon S3 uses the most restrictive combination of the bucket-level and\n account-level settings.

\n
\n

For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

\n

The following operations are related to GetPublicAccessBlock:

\n ", + "smithy.api#documentation": "

Retrieves the PublicAccessBlock configuration for an Amazon S3 bucket. To use\n this operation, you must have the s3:GetBucketPublicAccessBlock permission.\n For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n \n

When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or\n an object, it checks the PublicAccessBlock configuration for both the\n bucket (or the bucket that contains the object) and the bucket owner's account. If the\n PublicAccessBlock settings are different between the bucket and the\n account, Amazon S3 uses the most restrictive combination of the bucket-level and\n account-level settings.

\n
\n

For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

\n

The following operations are related to GetPublicAccessBlock:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?publicAccessBlock", @@ -25472,7 +27429,16 @@ } ], "traits": { - "smithy.api#documentation": "

This action is useful to determine if a bucket exists and you have permission to\n access it. The action returns a 200 OK if the bucket exists and you have\n permission to access it.

\n

If the bucket does not exist or you do not have permission to access it, the HEAD request\n returns a generic 404 Not Found or 403 Forbidden code. A message body is not \n included, so you cannot determine the exception beyond these error codes.

\n

To use this operation, you must have permissions to perform the\n s3:ListBucket action. The bucket owner has this permission by default and\n can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

To use this API against an access point, you must provide the alias of the access point in place of the bucket name or specify the access point ARN. When using the access point ARN, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using the Amazon Web Services SDKs, you provide the ARN in place of the bucket name. For more information see, Using access points.

", + "smithy.api#documentation": "

This action is useful to determine if a bucket exists and you have permission to access\n it. The action returns a 200 OK if the bucket exists and you have permission\n to access it.

\n

If the bucket does not exist or you do not have permission to access it, the\n HEAD request returns a generic 400 Bad Request, 403\n Forbidden or 404 Not Found code. A message body is not included, so\n you cannot determine the exception beyond these error codes.

\n

To use this operation, you must have permissions to perform the\n s3:ListBucket action. The bucket owner has this permission by default and\n can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

To use this API operation against an access point, you must provide the alias of the access point in place of the\n bucket name or specify the access point ARN. When using the access point ARN, you must direct requests to\n the access point hostname. The access point hostname takes the form\n AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com.\n When using the Amazon Web Services SDKs, you provide the ARN in place of the bucket name. For more\n information, see Using access points.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", + "smithy.api#examples": [ + { + "title": "To determine if bucket exists", + "documentation": "This operation checks to see if a bucket exists.", + "input": { + "Bucket": "acl1" + } + } + ], "smithy.api#http": { "method": "HEAD", "uri": "/{Bucket}", @@ -25516,7 +27482,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \n If the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \n For more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -25550,7 +27516,7 @@ } ], "traits": { - "smithy.api#documentation": "

The HEAD action retrieves metadata from an object without returning the object\n itself. This action is useful if you're only interested in an object's metadata. To use\n HEAD, you must have READ access to the object.

\n

A HEAD request has the same options as a GET action on an\n object. The response is identical to the GET response except that there is no\n response body. Because of this, if the HEAD request generates an error, it\n returns a generic 404 Not Found or 403 Forbidden code. It is not \n possible to retrieve the exact exception beyond these error codes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption (Using\n Customer-Provided Encryption Keys).

\n \n
    \n
  • \n

    Encryption request headers, like x-amz-server-side-encryption, should\n not be sent for GET requests if your object uses server-side encryption with KMS keys (SSE-KMS)\n or server-side encryption with Amazon S3–managed encryption keys\n (SSE-S3). If your object does use these types of keys, you’ll get an HTTP 400 BadRequest\n error.

    \n
  • \n
  • \n

    \n The last modified property in this case is the creation date of the object.

    \n
  • \n
\n
\n

Request headers are limited to 8 KB in size. For more information, see Common Request\n Headers.

\n

Consider the following when using request headers:

\n
    \n
  • \n

    Consideration 1 – If both of the If-Match and\n If-Unmodified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true, and;

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false;

      \n
    • \n
    \n

    Then Amazon S3 returns 200 OK and the data requested.

    \n
  • \n
  • \n

    Consideration 2 – If both of the If-None-Match and\n If-Modified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to false,\n and;

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true;

      \n
    • \n
    \n

    Then Amazon S3 returns the 304 Not Modified response code.

    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n

\n Permissions\n

\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Specifying Permissions\n in a Policy. If the object you request does not exist, the error Amazon S3 returns\n depends on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 returns\n an HTTP status code 404 (\"no such key\") error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP\n status code 403 (\"access denied\") error.

    \n
  • \n
\n

The following actions are related to HeadObject:

\n ", + "smithy.api#documentation": "

The HEAD action retrieves metadata from an object without returning the object itself.\n This action is useful if you're only interested in an object's metadata. To use HEAD, you\n must have READ access to the object.

\n

A HEAD request has the same options as a GET action on an\n object. The response is identical to the GET response except that there is no\n response body. Because of this, if the HEAD request generates an error, it\n returns a generic 400 Bad Request, 403 Forbidden or 404 Not\n Found code. It is not possible to retrieve the exact exception beyond these error\n codes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    \n x-amz-server-side-encryption-customer-algorithm\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key-MD5\n

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys).

\n \n
    \n
  • \n

    Encryption request headers, like x-amz-server-side-encryption,\n should not be sent for GET requests if your object uses server-side\n encryption with Key Management Service (KMS) keys (SSE-KMS), dual-layer server-side\n encryption with Amazon Web Services KMS keys (DSSE-KMS), or server-side encryption with Amazon S3\n managed encryption keys (SSE-S3). If your object does use these types of keys,\n you’ll get an HTTP 400 Bad Request error.

    \n
  • \n
  • \n

    The last modified property in this case is the creation date of the\n object.

    \n
  • \n
\n
\n

Request headers are limited to 8 KB in size. For more information, see Common\n Request Headers.

\n

Consider the following when using request headers:

\n
    \n
  • \n

    Consideration 1 – If both of the If-Match and\n If-Unmodified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true, and;

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false;

      \n
    • \n
    \n

    Then Amazon S3 returns 200 OK and the data requested.

    \n
  • \n
  • \n

    Consideration 2 – If both of the If-None-Match and\n If-Modified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to false,\n and;

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true;

      \n
    • \n
    \n

    Then Amazon S3 returns the 304 Not Modified response code.

    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n
\n
Permissions
\n
\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Actions, resources, and condition keys for Amazon S3. \n If the object you request doesn't exist, the error that Amazon S3 returns depends\n on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 returns\n an HTTP status code 404 error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP\n status code 403 error.

    \n
  • \n
\n
\n
\n

The following actions are related to HeadObject:

\n ", "smithy.api#http": { "method": "HEAD", "uri": "/{Bucket}/{Key+}", @@ -25744,7 +27710,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

If the object is stored using server-side encryption either with an Amazon Web Services KMS key or \n an Amazon S3-managed encryption key, the response includes this header with\n the value of the server-side encryption algorithm used when storing this object in Amazon\n S3 (for example, AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -25772,7 +27738,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -25780,14 +27746,14 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with\n Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

Provides storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage\n Classes.

", + "smithy.api#documentation": "

Provides storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage Classes.

", "smithy.api#httpHeader": "x-amz-storage-class" } }, @@ -25800,7 +27766,7 @@ "ReplicationStatus": { "target": "com.amazonaws.s3#ReplicationStatus", "traits": { - "smithy.api#documentation": "

Amazon S3 can return this header if your request involves a bucket that is either a source or\n a destination in a replication rule.

\n

In replication, you have a source bucket on which you configure replication and\n destination bucket or buckets where Amazon S3 stores object replicas. When you request an object\n (GetObject) or object metadata (HeadObject) from these\n buckets, Amazon S3 will return the x-amz-replication-status header in the response\n as follows:

\n
    \n
  • \n

    \n If requesting an object from the source bucket, Amazon S3 will return the\n x-amz-replication-status header if the object in your request is\n eligible for replication.

    \n

    For example, suppose that in your replication configuration, you specify object\n prefix TaxDocs requesting Amazon S3 to replicate objects with key prefix\n TaxDocs. Any objects you upload with this key name prefix, for\n example TaxDocs/document1.pdf, are eligible for replication. For any\n object request with this key name prefix, Amazon S3 will return the\n x-amz-replication-status header with value PENDING, COMPLETED or\n FAILED indicating object replication status.

    \n
  • \n
  • \n

    \n If requesting an object from a destination bucket, Amazon S3 will return the\n x-amz-replication-status header with value REPLICA if the object in\n your request is a replica that Amazon S3 created and there is no replica modification\n replication in progress.

    \n
  • \n
  • \n

    \n When replicating objects to multiple destination buckets, the\n x-amz-replication-status header acts differently. The header of the\n source object will only return a value of COMPLETED when replication is successful to\n all destinations. The header will remain at value PENDING until replication has\n completed for all destinations. If one or more destinations fails replication the\n header will return FAILED.

    \n
  • \n
\n

For more information, see Replication.

", + "smithy.api#documentation": "

Amazon S3 can return this header if your request involves a bucket that is either a source or\n a destination in a replication rule.

\n

In replication, you have a source bucket on which you configure replication and\n destination bucket or buckets where Amazon S3 stores object replicas. When you request an object\n (GetObject) or object metadata (HeadObject) from these\n buckets, Amazon S3 will return the x-amz-replication-status header in the response\n as follows:

\n
    \n
  • \n

    \n If requesting an object from the source bucket,\n Amazon S3 will return the x-amz-replication-status header if the object in\n your request is eligible for replication.

    \n

    For example, suppose that in your replication configuration, you specify object\n prefix TaxDocs requesting Amazon S3 to replicate objects with key prefix\n TaxDocs. Any objects you upload with this key name prefix, for\n example TaxDocs/document1.pdf, are eligible for replication. For any\n object request with this key name prefix, Amazon S3 will return the\n x-amz-replication-status header with value PENDING, COMPLETED or\n FAILED indicating object replication status.

    \n
  • \n
  • \n

    \n If requesting an object from a destination\n bucket, Amazon S3 will return the x-amz-replication-status header\n with value REPLICA if the object in your request is a replica that Amazon S3 created and\n there is no replica modification replication in progress.

    \n
  • \n
  • \n

    \n When replicating objects to multiple destination\n buckets, the x-amz-replication-status header acts\n differently. The header of the source object will only return a value of COMPLETED\n when replication is successful to all destinations. The header will remain at value\n PENDING until replication has completed for all destinations. If one or more\n destinations fails replication the header will return FAILED.

    \n
  • \n
\n

For more information, see Replication.

", "smithy.api#httpHeader": "x-amz-replication-status" } }, @@ -25808,14 +27774,14 @@ "target": "com.amazonaws.s3#PartsCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The count of parts this object has. This value is only returned if you specify partNumber\n in your request and the object was uploaded as a multipart upload.

", + "smithy.api#documentation": "

The count of parts this object has. This value is only returned if you specify\n partNumber in your request and the object was uploaded as a multipart\n upload.

", "smithy.api#httpHeader": "x-amz-mp-parts-count" } }, "ObjectLockMode": { "target": "com.amazonaws.s3#ObjectLockMode", "traits": { - "smithy.api#documentation": "

The Object Lock mode, if any, that's in effect for this object. This header is only\n returned if the requester has the s3:GetObjectRetention permission. For more\n information about S3 Object Lock, see Object\n Lock.

", + "smithy.api#documentation": "

The Object Lock mode, if any, that's in effect for this object. This header is only\n returned if the requester has the s3:GetObjectRetention permission. For more\n information about S3 Object Lock, see Object Lock.

", "smithy.api#httpHeader": "x-amz-object-lock-mode" } }, @@ -25844,7 +27810,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -25891,7 +27857,7 @@ "Range": { "target": "com.amazonaws.s3#Range", "traits": { - "smithy.api#documentation": "

Because HeadObject returns only the metadata for an object, this parameter\n has no effect.

", + "smithy.api#documentation": "

HeadObject returns only the metadata for an object. If the Range is satisfiable, only\n the ContentLength is affected in the response. If the Range is not\n satisfiable, S3 returns a 416 - Requested Range Not Satisfiable error.

", "smithy.api#httpHeader": "Range" } }, @@ -25947,7 +27913,7 @@ "ChecksumMode": { "target": "com.amazonaws.s3#ChecksumMode", "traits": { - "smithy.api#documentation": "

To retrieve the checksum, this parameter must be enabled.

\n

In addition, if you enable ChecksumMode and the object is encrypted with\n Amazon Web Services Key Management Service (Amazon Web Services KMS), you must have permission to use the\n kms:Decrypt action for the request to succeed.

", + "smithy.api#documentation": "

To retrieve the checksum, this parameter must be enabled.

\n

In addition, if you enable ChecksumMode and the object is encrypted with\n Amazon Web Services Key Management Service (Amazon Web Services KMS), you must have permission to use the\n kms:Decrypt action for the request to succeed.

", "smithy.api#httpHeader": "x-amz-checksum-mode" } } @@ -26004,7 +27970,7 @@ "ID": { "target": "com.amazonaws.s3#ID", "traits": { - "smithy.api#documentation": "

If the principal is an Amazon Web Services account, it provides the Canonical User ID. If the principal\n is an IAM User, it provides a user ARN value.

" + "smithy.api#documentation": "

If the principal is an Amazon Web Services account, it provides the Canonical User ID. If the\n principal is an IAM User, it provides a user ARN value.

" } }, "DisplayName": { @@ -26123,7 +28089,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies the S3 Intelligent-Tiering configuration for an Amazon S3 bucket.

\n

For information about the S3 Intelligent-Tiering storage class, see Storage class for\n automatically optimizing frequently and infrequently accessed objects.

" + "smithy.api#documentation": "

Specifies the S3 Intelligent-Tiering configuration for an Amazon S3 bucket.

\n

For information about the S3 Intelligent-Tiering storage class, see Storage class\n for automatically optimizing frequently and infrequently accessed\n objects.

" } }, "com.amazonaws.s3#IntelligentTieringConfigurationList": { @@ -26249,7 +28215,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies the inventory configuration for an Amazon S3 bucket. For more information, see\n GET Bucket inventory in the Amazon S3 API Reference.\n

" + "smithy.api#documentation": "

Specifies the inventory configuration for an Amazon S3 bucket. For more information, see\n GET Bucket inventory in the Amazon S3 API Reference.

" } }, "com.amazonaws.s3#InventoryConfigurationList": { @@ -26650,7 +28616,7 @@ "Date": { "target": "com.amazonaws.s3#Date", "traits": { - "smithy.api#documentation": "

Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601\n Format.

" + "smithy.api#documentation": "

Indicates at what date the object is to be moved or deleted. The date value must conform to the ISO 8601 format. \n The time is always midnight UTC.

" } }, "Days": { @@ -26669,7 +28635,7 @@ } }, "traits": { - "smithy.api#documentation": "

Container for the expiration for the lifecycle of the object.

" + "smithy.api#documentation": "

Container for the expiration for the lifecycle of the object.

\n

For more information see, Managing your storage\n lifecycle in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3#LifecycleRule": { @@ -26731,7 +28697,7 @@ } }, "traits": { - "smithy.api#documentation": "

A lifecycle rule for individual objects in an Amazon S3 bucket.

" + "smithy.api#documentation": "

A lifecycle rule for individual objects in an Amazon S3 bucket.

\n

For more information see, Managing your storage\n lifecycle in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3#LifecycleRuleAndOperator": { @@ -26820,7 +28786,7 @@ "target": "com.amazonaws.s3#ListBucketAnalyticsConfigurationsOutput" }, "traits": { - "smithy.api#documentation": "

Lists the analytics configurations for the bucket. You can have up to 1,000 analytics\n configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations\n at a time. You should always check the IsTruncated element in the response. If\n there are no more configurations to list, IsTruncated is set to false. If\n there are more configurations to list, IsTruncated is set to true, and there\n will be a value in NextContinuationToken. You use the\n NextContinuationToken value to continue the pagination of the list by\n passing the value in continuation-token in the request to GET the next\n page.

\n

To use this operation, you must have permissions to perform the\n s3:GetAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis.

\n

The following operations are related to\n ListBucketAnalyticsConfigurations:

\n ", + "smithy.api#documentation": "

Lists the analytics configurations for the bucket. You can have up to 1,000 analytics\n configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations at\n a time. You should always check the IsTruncated element in the response. If\n there are no more configurations to list, IsTruncated is set to false. If\n there are more configurations to list, IsTruncated is set to true, and there\n will be a value in NextContinuationToken. You use the\n NextContinuationToken value to continue the pagination of the list by\n passing the value in continuation-token in the request to GET the next\n page.

\n

To use this operation, you must have permissions to perform the\n s3:GetAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis.

\n

The following operations are related to\n ListBucketAnalyticsConfigurations:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?analytics&x-id=ListBucketAnalyticsConfigurations", @@ -26906,7 +28872,7 @@ "target": "com.amazonaws.s3#ListBucketIntelligentTieringConfigurationsOutput" }, "traits": { - "smithy.api#documentation": "

Lists the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to\n ListBucketIntelligentTieringConfigurations include:

\n ", + "smithy.api#documentation": "

Lists the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to ListBucketIntelligentTieringConfigurations include:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?intelligent-tiering&x-id=ListBucketIntelligentTieringConfigurations", @@ -26984,7 +28950,7 @@ "target": "com.amazonaws.s3#ListBucketInventoryConfigurationsOutput" }, "traits": { - "smithy.api#documentation": "

Returns a list of inventory configurations for the bucket. You can have up to 1,000\n analytics configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations\n at a time. Always check the IsTruncated element in the response. If there are\n no more configurations to list, IsTruncated is set to false. If there are more\n configurations to list, IsTruncated is set to true, and there is a value in\n NextContinuationToken. You use the NextContinuationToken value\n to continue the pagination of the list by passing the value in continuation-token in the\n request to GET the next page.

\n

To use this operation, you must have permissions to perform the\n s3:GetInventoryConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory\n

\n

The following operations are related to\n ListBucketInventoryConfigurations:

\n ", + "smithy.api#documentation": "

Returns a list of inventory configurations for the bucket. You can have up to 1,000\n analytics configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations at\n a time. Always check the IsTruncated element in the response. If there are no\n more configurations to list, IsTruncated is set to false. If there are more\n configurations to list, IsTruncated is set to true, and there is a value in\n NextContinuationToken. You use the NextContinuationToken value\n to continue the pagination of the list by passing the value in continuation-token in the\n request to GET the next page.

\n

To use this operation, you must have permissions to perform the\n s3:GetInventoryConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory\n

\n

The following operations are related to\n ListBucketInventoryConfigurations:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?inventory&x-id=ListBucketInventoryConfigurations", @@ -27070,7 +29036,7 @@ "target": "com.amazonaws.s3#ListBucketMetricsConfigurationsOutput" }, "traits": { - "smithy.api#documentation": "

Lists the metrics configurations for the bucket. The metrics configurations are only for\n the request metrics of the bucket and do not provide information on daily storage metrics.\n You can have up to 1,000 configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations\n at a time. Always check the IsTruncated element in the response. If there are\n no more configurations to list, IsTruncated is set to false. If there are more\n configurations to list, IsTruncated is set to true, and there is a value in\n NextContinuationToken. You use the NextContinuationToken value\n to continue the pagination of the list by passing the value in\n continuation-token in the request to GET the next page.

\n

To use this operation, you must have permissions to perform the\n s3:GetMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For more information about metrics configurations and CloudWatch request metrics, see\n Monitoring Metrics with Amazon\n CloudWatch.

\n

The following operations are related to\n ListBucketMetricsConfigurations:

\n ", + "smithy.api#documentation": "

Lists the metrics configurations for the bucket. The metrics configurations are only for\n the request metrics of the bucket and do not provide information on daily storage metrics.\n You can have up to 1,000 configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations at\n a time. Always check the IsTruncated element in the response. If there are no\n more configurations to list, IsTruncated is set to false. If there are more\n configurations to list, IsTruncated is set to true, and there is a value in\n NextContinuationToken. You use the NextContinuationToken value\n to continue the pagination of the list by passing the value in\n continuation-token in the request to GET the next page.

\n

To use this operation, you must have permissions to perform the\n s3:GetMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For more information about metrics configurations and CloudWatch request metrics, see\n Monitoring Metrics with Amazon CloudWatch.

\n

The following operations are related to\n ListBucketMetricsConfigurations:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?metrics&x-id=ListBucketMetricsConfigurations", @@ -27156,7 +29122,33 @@ "target": "com.amazonaws.s3#ListBucketsOutput" }, "traits": { - "smithy.api#documentation": "

Returns a list of all buckets owned by the authenticated sender of the request. To use\n this operation, you must have the s3:ListAllMyBuckets permission.

", + "smithy.api#documentation": "

Returns a list of all buckets owned by the authenticated sender of the request. To use\n this operation, you must have the s3:ListAllMyBuckets permission.

\n

For information about Amazon S3 buckets, see Creating, configuring, and\n working with Amazon S3 buckets.

", + "smithy.api#examples": [ + { + "title": "To list all buckets", + "documentation": "The following example returns all the buckets owned by the sender of this request.", + "output": { + "Owner": { + "DisplayName": "own-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31" + }, + "Buckets": [ + { + "CreationDate": "2012-02-15T21:03:02.000Z", + "Name": "examplebucket" + }, + { + "CreationDate": "2011-07-24T19:33:50.000Z", + "Name": "examplebucket2" + }, + { + "CreationDate": "2010-12-17T00:56:49.000Z", + "Name": "examplebucket3" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/", @@ -27194,7 +29186,7 @@ "target": "com.amazonaws.s3#ListMultipartUploadsOutput" }, "traits": { - "smithy.api#documentation": "

This action lists in-progress multipart uploads. An in-progress multipart upload is a\n multipart upload that has been initiated using the Initiate Multipart Upload request, but\n has not yet been completed or aborted.

\n

This action returns at most 1,000 multipart uploads in the response. 1,000 multipart\n uploads is the maximum number of uploads a response can include, which is also the default\n value. You can further limit the number of uploads in a response by specifying the\n max-uploads parameter in the response. If additional multipart uploads\n satisfy the list criteria, the response will contain an IsTruncated element\n with the value true. To list the additional multipart uploads, use the\n key-marker and upload-id-marker request parameters.

\n

In the response, the uploads are sorted by key. If your application has initiated more\n than one multipart upload using the same object key, then uploads in the response are first\n sorted by key. Additionally, uploads are sorted in ascending order within each key by the\n upload initiation time.

\n

For more information on multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information on permissions required to use the multipart upload API, see Multipart Upload and\n Permissions.

\n

The following operations are related to ListMultipartUploads:

\n ", + "smithy.api#documentation": "

This action lists in-progress multipart uploads. An in-progress multipart upload is a\n multipart upload that has been initiated using the Initiate Multipart Upload request, but\n has not yet been completed or aborted.

\n

This action returns at most 1,000 multipart uploads in the response. 1,000 multipart\n uploads is the maximum number of uploads a response can include, which is also the default\n value. You can further limit the number of uploads in a response by specifying the\n max-uploads parameter in the response. If additional multipart uploads\n satisfy the list criteria, the response will contain an IsTruncated element\n with the value true. To list the additional multipart uploads, use the\n key-marker and upload-id-marker request parameters.

\n

In the response, the uploads are sorted by key. If your application has initiated more\n than one multipart upload using the same object key, then uploads in the response are first\n sorted by key. Additionally, uploads are sorted in ascending order within each key by the\n upload initiation time.

\n

For more information on multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information on permissions required to use the multipart upload API, see Multipart Upload\n and Permissions.

\n

The following operations are related to ListMultipartUploads:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?uploads", @@ -27208,7 +29200,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the access point ARN or access point alias if used.

" + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the\n access point ARN or access point alias if used.

" } }, "KeyMarker": { @@ -27281,6 +29273,12 @@ "traits": { "smithy.api#documentation": "

Encoding type used by Amazon S3 to encode object keys in the response.

\n

If you specify encoding-type request parameter, Amazon S3 includes this element\n in the response, and returns encoded key name values in the following response\n elements:

\n

\n Delimiter, KeyMarker, Prefix,\n NextKeyMarker, Key.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -27294,7 +29292,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -27350,6 +29348,12 @@ "smithy.api#documentation": "

The account ID of the expected bucket owner. If the bucket is owned by a different account, the request fails with the HTTP status code 403 Forbidden (access denied).

", "smithy.api#httpHeader": "x-amz-expected-bucket-owner" } + }, + "RequestPayer": { + "target": "com.amazonaws.s3#RequestPayer", + "traits": { + "smithy.api#httpHeader": "x-amz-request-payer" + } } }, "traits": { @@ -27365,7 +29369,47 @@ "target": "com.amazonaws.s3#ListObjectVersionsOutput" }, "traits": { - "smithy.api#documentation": "

Returns metadata about all versions of the objects in a bucket. You can also use request\n parameters as selection criteria to return metadata about a subset of all the object\n versions.

\n \n

\n To use this operation, you must have permissions to perform the \n s3:ListBucketVersions action. Be aware of the name difference.\n

\n
\n \n

A 200 OK response can contain valid or invalid XML. Make sure to design your\n application to parse the contents of the response and handle it appropriately.

\n
\n

To use this operation, you must have READ access to the bucket.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following operations are related to\n ListObjectVersions:

\n ", + "smithy.api#documentation": "

Returns metadata about all versions of the objects in a bucket. You can also use request\n parameters as selection criteria to return metadata about a subset of all the object\n versions.

\n \n

To use this operation, you must have permissions to perform the\n s3:ListBucketVersions action. Be aware of the name difference.

\n
\n \n

A 200 OK response can contain valid or invalid XML. Make sure to design your\n application to parse the contents of the response and handle it appropriately.

\n
\n

To use this operation, you must have READ access to the bucket.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following operations are related to ListObjectVersions:

\n ", + "smithy.api#examples": [ + { + "title": "To list object versions", + "documentation": "The following example return versions of an object with specific key name prefix. The request limits the number of items returned to two. If there are are more than two object version, S3 returns NextToken in the response. You can specify this token value in your next request to fetch next set of object versions.", + "input": { + "Bucket": "examplebucket", + "Prefix": "HappyFace.jpg" + }, + "output": { + "Versions": [ + { + "LastModified": "2016-12-15T01:19:41.000Z", + "VersionId": "null", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "StorageClass": "STANDARD", + "Key": "HappyFace.jpg", + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "IsLatest": true, + "Size": 3191 + }, + { + "LastModified": "2016-12-13T00:58:26.000Z", + "VersionId": "PHtexPGjH2y.zBgT8LmB7wwLI2mpbz.k", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "StorageClass": "STANDARD", + "Key": "HappyFace.jpg", + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "IsLatest": false, + "Size": 3191 + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?versions", @@ -27460,6 +29504,12 @@ "traits": { "smithy.api#documentation": "

Encoding type used by Amazon S3 to encode object key names in the XML response.

\n

If you specify encoding-type request parameter, Amazon S3 includes this element in the\n response, and returns encoded key name values in the following response elements:

\n

\n KeyMarker, NextKeyMarker, Prefix, Key, and Delimiter.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -27505,7 +29555,7 @@ "target": "com.amazonaws.s3#MaxKeys", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns up\n to 1,000 key names. The response might contain fewer keys but will never contain more. If\n additional keys satisfy the search criteria, but were not returned because max-keys was\n exceeded, the response contains true. To return the\n additional keys, see key-marker and version-id-marker.

", + "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns\n up to 1,000 key names. The response might contain fewer keys but will never contain more.\n If additional keys satisfy the search criteria, but were not returned because max-keys was\n exceeded, the response contains true. To return the\n additional keys, see key-marker and version-id-marker.

", "smithy.api#httpQuery": "max-keys" } }, @@ -27529,6 +29579,12 @@ "smithy.api#documentation": "

The account ID of the expected bucket owner. If the bucket is owned by a different account, the request fails with the HTTP status code 403 Forbidden (access denied).

", "smithy.api#httpHeader": "x-amz-expected-bucket-owner" } + }, + "RequestPayer": { + "target": "com.amazonaws.s3#RequestPayer", + "traits": { + "smithy.api#httpHeader": "x-amz-request-payer" + } } }, "traits": { @@ -27614,7 +29670,7 @@ "CommonPrefixes": { "target": "com.amazonaws.s3#CommonPrefixList", "traits": { - "smithy.api#documentation": "

All of the keys (up to 1,000) rolled up in a common prefix count as a single return when calculating\n the number of returns.

\n

A response can contain CommonPrefixes only if you specify a delimiter.

\n

CommonPrefixes contains all (if there are any) keys between Prefix and the next\n occurrence of the string specified by the delimiter.

\n

CommonPrefixes lists keys that act like subdirectories in the directory specified by\n Prefix.

\n

For example, if the prefix is notes/ and the delimiter is a slash (/) as in\n notes/summer/july, the common prefix is notes/summer/. All of the keys that roll up into a\n common prefix count as a single return when calculating the number of returns.

", + "smithy.api#documentation": "

All of the keys (up to 1,000) rolled up in a common prefix count as a single return when\n calculating the number of returns.

\n

A response can contain CommonPrefixes only if you specify a delimiter.

\n

CommonPrefixes contains all (if there are any) keys between Prefix and the next\n occurrence of the string specified by the delimiter.

\n

CommonPrefixes lists keys that act like subdirectories in the directory specified by\n Prefix.

\n

For example, if the prefix is notes/ and the delimiter is a slash (/) as in\n notes/summer/july, the common prefix is notes/summer/. All of the keys that roll up into a\n common prefix count as a single return when calculating the number of returns.

", "smithy.api#xmlFlattened": {} } }, @@ -27623,6 +29679,12 @@ "traits": { "smithy.api#documentation": "

Encoding type used by Amazon S3 to encode object keys in the response.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -27636,7 +29698,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket containing the objects.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket containing the objects.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -27660,7 +29722,7 @@ "Marker": { "target": "com.amazonaws.s3#Marker", "traits": { - "smithy.api#documentation": "

Marker is where you want Amazon S3 to start listing from. Amazon S3 starts listing after\n this specified key. Marker can be any key in the bucket.

", + "smithy.api#documentation": "

Marker is where you want Amazon S3 to start listing from. Amazon S3 starts listing after\n this specified key. Marker can be any key in the bucket.

", "smithy.api#httpQuery": "marker" } }, @@ -27668,7 +29730,7 @@ "target": "com.amazonaws.s3#MaxKeys", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns up\n to 1,000 key names. The response might contain fewer keys but will never contain more.\n

", + "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns\n up to 1,000 key names. The response might contain fewer keys but will never contain more.\n

", "smithy.api#httpQuery": "max-keys" } }, @@ -27712,7 +29774,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns some or all (up to 1,000) of the objects in a bucket with each request. You can use\n the request parameters as selection criteria to return a subset of the objects in a bucket. A \n 200 OK response can contain valid or invalid XML. Make sure to design your\n application to parse the contents of the response and handle it appropriately. \n Objects are returned sorted in an ascending order of the respective key names in the list.\n For more information about listing objects, see Listing object keys \n programmatically\n

\n

To use this operation, you must have READ access to the bucket.

\n

To use this action in an Identity and Access Management (IAM) policy, you must\n have permissions to perform the s3:ListBucket action. The bucket owner has\n this permission by default and can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n \n

This section describes the latest revision of this action. We recommend that you use this\n revised API for application development. For backward compatibility, Amazon S3 continues to\n support the prior version of this API, ListObjects.

\n
\n

To get a list of your buckets, see ListBuckets.

\n

The following operations are related to ListObjectsV2:

\n ", + "smithy.api#documentation": "

Returns some or all (up to 1,000) of the objects in a bucket with each request. You can\n use the request parameters as selection criteria to return a subset of the objects in a\n bucket. A 200 OK response can contain valid or invalid XML. Make sure to\n design your application to parse the contents of the response and handle it appropriately.\n Objects are returned sorted in an ascending order of the respective key names in the list.\n For more information about listing objects, see Listing object keys\n programmatically\n

\n

To use this operation, you must have READ access to the bucket.

\n

To use this action in an Identity and Access Management (IAM) policy, you must have permissions to perform\n the s3:ListBucket action. The bucket owner has this permission by default and\n can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n \n

This section describes the latest revision of this action. We recommend that you use\n this revised API for application development. For backward compatibility, Amazon S3 continues\n to support the prior version of this API, ListObjects.

\n
\n

To get a list of your buckets, see ListBuckets.

\n

The following operations are related to ListObjectsV2:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?list-type=2", @@ -27745,7 +29807,7 @@ "Name": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

" } }, "Prefix": { @@ -27764,13 +29826,13 @@ "target": "com.amazonaws.s3#MaxKeys", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns up\n to 1,000 key names. The response might contain fewer keys but will never contain\n more.

" + "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns\n up to 1,000 key names. The response might contain fewer keys but will never contain\n more.

" } }, "CommonPrefixes": { "target": "com.amazonaws.s3#CommonPrefixList", "traits": { - "smithy.api#documentation": "

All of the keys (up to 1,000) rolled up into a common prefix count as a single return when calculating\n the number of returns.

\n

A response can contain CommonPrefixes only if you specify a\n delimiter.

\n

\n CommonPrefixes contains all (if there are any) keys between\n Prefix and the next occurrence of the string specified by a\n delimiter.

\n

\n CommonPrefixes lists keys that act like subdirectories in the directory\n specified by Prefix.

\n

For example, if the prefix is notes/ and the delimiter is a slash\n (/) as in notes/summer/july, the common prefix is\n notes/summer/. All of the keys that roll up into a common prefix count as a\n single return when calculating the number of returns.

", + "smithy.api#documentation": "

All of the keys (up to 1,000) rolled up into a common prefix count as a single return\n when calculating the number of returns.

\n

A response can contain CommonPrefixes only if you specify a\n delimiter.

\n

\n CommonPrefixes contains all (if there are any) keys between\n Prefix and the next occurrence of the string specified by a\n delimiter.

\n

\n CommonPrefixes lists keys that act like subdirectories in the directory\n specified by Prefix.

\n

For example, if the prefix is notes/ and the delimiter is a slash\n (/) as in notes/summer/july, the common prefix is\n notes/summer/. All of the keys that roll up into a common prefix count as a\n single return when calculating the number of returns.

", "smithy.api#xmlFlattened": {} } }, @@ -27784,7 +29846,7 @@ "target": "com.amazonaws.s3#KeyCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

KeyCount is the number of keys returned with this request. KeyCount will always be less\n than or equals to MaxKeys field. Say you ask for 50 keys, your result will include less than\n equals 50 keys

" + "smithy.api#documentation": "

KeyCount is the number of keys returned with this request. KeyCount will always be less\n than or equal to the MaxKeys field. Say you ask for 50 keys, your result will\n include 50 keys or fewer.

" } }, "ContinuationToken": { @@ -27804,6 +29866,12 @@ "traits": { "smithy.api#documentation": "

If StartAfter was sent with the request, it is included in the response.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -27817,7 +29885,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Bucket name to list.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Bucket name to list.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -27843,7 +29911,7 @@ "target": "com.amazonaws.s3#MaxKeys", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns up\n to 1,000 key names. The response might contain fewer keys but will never contain\n more.

", + "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns\n up to 1,000 key names. The response might contain fewer keys but will never contain\n more.

", "smithy.api#httpQuery": "max-keys" } }, @@ -27904,7 +29972,7 @@ "target": "com.amazonaws.s3#ListPartsOutput" }, "traits": { - "smithy.api#documentation": "

Lists the parts that have been uploaded for a specific multipart upload. This operation\n must include the upload ID, which you obtain by sending the initiate multipart upload\n request (see CreateMultipartUpload).\n This request returns a maximum of 1,000 uploaded parts. The default number of parts\n returned is 1,000 parts. You can restrict the number of parts returned by specifying the\n max-parts request parameter. If your multipart upload consists of more than\n 1,000 parts, the response returns an IsTruncated field with the value of true,\n and a NextPartNumberMarker element. In subsequent ListParts\n requests you can include the part-number-marker query string parameter and set its value to\n the NextPartNumberMarker field value from the previous response.

\n

If the upload was created using a checksum algorithm, you will need to have permission\n to the kms:Decrypt action for the request to succeed.\n

\n

For more information on multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information on permissions required to use the multipart upload API, see Multipart Upload and\n Permissions.

\n

The following operations are related to ListParts:

\n ", + "smithy.api#documentation": "

Lists the parts that have been uploaded for a specific multipart upload. This operation\n must include the upload ID, which you obtain by sending the initiate multipart upload\n request (see CreateMultipartUpload).\n This request returns a maximum of 1,000 uploaded parts. The default number of parts\n returned is 1,000 parts. You can restrict the number of parts returned by specifying the\n max-parts request parameter. If your multipart upload consists of more than\n 1,000 parts, the response returns an IsTruncated field with the value of true,\n and a NextPartNumberMarker element. In subsequent ListParts\n requests you can include the part-number-marker query string parameter and set its value to\n the NextPartNumberMarker field value from the previous response.

\n

If the upload was created using a checksum algorithm, you will need to have permission\n to the kms:Decrypt action for the request to succeed.

\n

For more information on multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information on permissions required to use the multipart upload API, see Multipart Upload\n and Permissions.

\n

The following operations are related to ListParts:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?x-id=ListParts", @@ -27924,7 +29992,7 @@ "AbortDate": { "target": "com.amazonaws.s3#AbortDate", "traits": { - "smithy.api#documentation": "

If the bucket has a lifecycle rule configured with an action to abort incomplete\n multipart uploads and the prefix in the lifecycle rule matches the object name in the\n request, then the response includes this header indicating when the initiated multipart\n upload will become eligible for abort operation. For more information, see Aborting\n Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

\n

The response will also include the x-amz-abort-rule-id header that will\n provide the ID of the lifecycle configuration rule that defines this action.

", + "smithy.api#documentation": "

If the bucket has a lifecycle rule configured with an action to abort incomplete\n multipart uploads and the prefix in the lifecycle rule matches the object name in the\n request, then the response includes this header indicating when the initiated multipart\n upload will become eligible for abort operation. For more information, see Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration.

\n

The response will also include the x-amz-abort-rule-id header that will\n provide the ID of the lifecycle configuration rule that defines this action.

", "smithy.api#httpHeader": "x-amz-abort-date" } }, @@ -27938,7 +30006,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the access point ARN or access point alias if used.

" + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the\n access point ARN or access point alias if used.

" } }, "Key": { @@ -28029,7 +30097,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the parts are being uploaded.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which the parts are being uploaded.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -28126,7 +30194,7 @@ "TargetGrants": { "target": "com.amazonaws.s3#TargetGrants", "traits": { - "smithy.api#documentation": "

Container for granting information.

\n

Buckets that use the bucket owner enforced setting for Object\n Ownership don't support target grants. For more information, see Permissions for server access log delivery in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Container for granting information.

\n

Buckets that use the bucket owner enforced setting for Object Ownership don't support\n target grants. For more information, see Permissions for server access log delivery in the\n Amazon S3 User Guide.

" } }, "TargetPrefix": { @@ -28315,14 +30383,14 @@ "Id": { "target": "com.amazonaws.s3#MetricsId", "traits": { - "smithy.api#documentation": "

The ID used to identify the metrics configuration.

", + "smithy.api#documentation": "

The ID used to identify the metrics configuration. The ID has a 64 character limit and\n can only contain letters, numbers, periods, dashes, and underscores.

", "smithy.api#required": {} } }, "Filter": { "target": "com.amazonaws.s3#MetricsFilter", "traits": { - "smithy.api#documentation": "

Specifies a metrics configuration filter. The metrics configuration will only include\n objects that meet the filter's criteria. A filter must be a prefix, an object tag, an access point ARN, or a conjunction\n (MetricsAndOperator).

" + "smithy.api#documentation": "

Specifies a metrics configuration filter. The metrics configuration will only include\n objects that meet the filter's criteria. A filter must be a prefix, an object tag, an\n access point ARN, or a conjunction (MetricsAndOperator).

" } } }, @@ -28365,7 +30433,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies a metrics configuration filter. The metrics configuration only includes\n objects that meet the filter's criteria. A filter must be a prefix, an object tag, an access point ARN, or a conjunction\n (MetricsAndOperator). For more information, see PutBucketMetricsConfiguration.

" + "smithy.api#documentation": "

Specifies a metrics configuration filter. The metrics configuration only includes\n objects that meet the filter's criteria. A filter must be a prefix, an object tag, an\n access point ARN, or a conjunction (MetricsAndOperator). For more information, see PutBucketMetricsConfiguration.

" } }, "com.amazonaws.s3#MetricsId": { @@ -28508,14 +30576,14 @@ "target": "com.amazonaws.s3#Days", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies the number of days an object is noncurrent before Amazon S3 can perform the\n associated action. The value must be a non-zero positive integer. For information about the noncurrent days calculations, see How\n Amazon S3 Calculates When an Object Became Noncurrent in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies the number of days an object is noncurrent before Amazon S3 can perform the\n associated action. The value must be a non-zero positive integer. For information about the\n noncurrent days calculations, see How\n Amazon S3 Calculates When an Object Became Noncurrent in the\n Amazon S3 User Guide.

" } }, "NewerNoncurrentVersions": { "target": "com.amazonaws.s3#VersionCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies how many noncurrent versions Amazon S3 will retain. If there are this many more recent\n noncurrent versions, Amazon S3 will take the associated action. For more information about noncurrent\n versions, see Lifecycle configuration elements\n in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies how many noncurrent versions Amazon S3 will retain. If there are this many more\n recent noncurrent versions, Amazon S3 will take the associated action. For more information\n about noncurrent versions, see Lifecycle configuration\n elements in the Amazon S3 User Guide.

" } } }, @@ -28543,12 +30611,12 @@ "target": "com.amazonaws.s3#VersionCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies how many noncurrent versions Amazon S3 will retain. If there are this many more recent\n noncurrent versions, Amazon S3 will take the associated action. For more information about noncurrent\n versions, see Lifecycle configuration elements\n in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies how many noncurrent versions Amazon S3 will retain. If there are this many more\n recent noncurrent versions, Amazon S3 will take the associated action. For more information\n about noncurrent versions, see Lifecycle configuration\n elements in the Amazon S3 User Guide.

" } } }, "traits": { - "smithy.api#documentation": "

Container for the transition rule that describes when noncurrent objects transition to\n the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,\n GLACIER_IR, GLACIER, or DEEP_ARCHIVE storage class. If your bucket is\n versioning-enabled (or versioning is suspended), you can set this action to request that\n Amazon S3 transition noncurrent object versions to the STANDARD_IA,\n ONEZONE_IA, INTELLIGENT_TIERING, GLACIER_IR, GLACIER, or\n DEEP_ARCHIVE storage class at a specific period in the object's\n lifetime.

" + "smithy.api#documentation": "

Container for the transition rule that describes when noncurrent objects transition to\n the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,\n GLACIER_IR, GLACIER, or DEEP_ARCHIVE storage\n class. If your bucket is versioning-enabled (or versioning is suspended), you can set this\n action to request that Amazon S3 transition noncurrent object versions to the\n STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,\n GLACIER_IR, GLACIER, or DEEP_ARCHIVE storage\n class at a specific period in the object's lifetime.

" } }, "com.amazonaws.s3#NoncurrentVersionTransitionList": { @@ -28614,7 +30682,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies object key name filtering rules. For information about key name filtering, see\n Configuring\n Event Notifications in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies object key name filtering rules. For information about key name filtering, see\n Configuring event notifications using object key name filtering in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3#NotificationId": { @@ -28641,7 +30709,7 @@ "ETag": { "target": "com.amazonaws.s3#ETag", "traits": { - "smithy.api#documentation": "

The entity tag is a hash of the object. The ETag reflects changes only to the contents\n of an object, not its metadata. The ETag may or may not be an MD5 digest of the object\n data. Whether or not it is depends on how the object was created and how it is encrypted as\n described below:

\n
    \n
  • \n

    Objects created by the PUT Object, POST Object, or Copy operation, or through the\n Amazon Web Services Management Console, and are encrypted by SSE-S3 or plaintext, have ETags that are\n an MD5 digest of their object data.

    \n
  • \n
  • \n

    Objects created by the PUT Object, POST Object, or Copy operation, or through the\n Amazon Web Services Management Console, and are encrypted by SSE-C or SSE-KMS, have ETags that are\n not an MD5 digest of their object data.

    \n
  • \n
  • \n

    If an object is created by either the Multipart Upload or Part Copy operation, the\n ETag is not an MD5 digest, regardless of the method of encryption. If an object\n is larger than 16 MB, the Amazon Web Services Management Console will upload or copy that object as a\n Multipart Upload, and therefore the ETag will not be an MD5 digest.

    \n
  • \n
" + "smithy.api#documentation": "

The entity tag is a hash of the object. The ETag reflects changes only to the contents\n of an object, not its metadata. The ETag may or may not be an MD5 digest of the object\n data. Whether or not it is depends on how the object was created and how it is encrypted as\n described below:

\n
    \n
  • \n

    Objects created by the PUT Object, POST Object, or Copy operation, or through the\n Amazon Web Services Management Console, and are encrypted by SSE-S3 or plaintext, have ETags that\n are an MD5 digest of their object data.

    \n
  • \n
  • \n

    Objects created by the PUT Object, POST Object, or Copy operation, or through the\n Amazon Web Services Management Console, and are encrypted by SSE-C or SSE-KMS, have ETags that are\n not an MD5 digest of their object data.

    \n
  • \n
  • \n

    If an object is created by either the Multipart Upload or Part Copy operation, the\n ETag is not an MD5 digest, regardless of the method of encryption. If an object is\n larger than 16 MB, the Amazon Web Services Management Console will upload or copy that object as a\n Multipart Upload, and therefore the ETag will not be an MD5 digest.

    \n
  • \n
" } }, "ChecksumAlgorithm": { @@ -28818,13 +30886,13 @@ "ObjectLockEnabled": { "target": "com.amazonaws.s3#ObjectLockEnabled", "traits": { - "smithy.api#documentation": "

Indicates whether this bucket has an Object Lock configuration enabled.\n Enable ObjectLockEnabled when you apply ObjectLockConfiguration\n to a bucket.

" + "smithy.api#documentation": "

Indicates whether this bucket has an Object Lock configuration enabled. Enable\n ObjectLockEnabled when you apply ObjectLockConfiguration to a\n bucket.

" } }, "Rule": { "target": "com.amazonaws.s3#ObjectLockRule", "traits": { - "smithy.api#documentation": "

Specifies the Object Lock rule for the specified object. Enable the this rule when you apply\n ObjectLockConfiguration to a bucket. Bucket settings require both a mode and a period.\n The period can be either Days or Years but you must select one.\n You cannot specify Days and Years at the same time.

" + "smithy.api#documentation": "

Specifies the Object Lock rule for the specified object. Enable the this rule when you\n apply ObjectLockConfiguration to a bucket. Bucket settings require both a mode\n and a period. The period can be either Days or Years but you must\n select one. You cannot specify Days and Years at the same\n time.

" } } }, @@ -28946,7 +31014,7 @@ "DefaultRetention": { "target": "com.amazonaws.s3#DefaultRetention", "traits": { - "smithy.api#documentation": "

The default Object Lock retention mode and period that you want to apply to new objects\n placed in the specified bucket. Bucket settings require both a mode and a period.\n The period can be either Days or Years but you must select one.\n You cannot specify Days and Years at the same time.

" + "smithy.api#documentation": "

The default Object Lock retention mode and period that you want to apply to new objects\n placed in the specified bucket. Bucket settings require both a mode and a period. The\n period can be either Days or Years but you must select one. You\n cannot specify Days and Years at the same time.

" } } }, @@ -28988,7 +31056,7 @@ } }, "traits": { - "smithy.api#documentation": "

The container element for object ownership for a bucket's ownership controls.

\n

BucketOwnerPreferred - Objects uploaded to the bucket change ownership to the bucket\n owner if the objects are uploaded with the bucket-owner-full-control canned\n ACL.

\n

ObjectWriter - The uploading account will own the object if the object is uploaded with\n the bucket-owner-full-control canned ACL.

\n

BucketOwnerEnforced - Access control lists (ACLs) are disabled and no longer affect permissions. \n The bucket owner automatically owns and has full control over every object in the bucket. The bucket only\n accepts PUT requests that don't specify an ACL or bucket owner full control\n ACLs, such as the bucket-owner-full-control canned\n ACL or an equivalent form of this ACL expressed in the XML format.

" + "smithy.api#documentation": "

The container element for object ownership for a bucket's ownership controls.

\n

BucketOwnerPreferred - Objects uploaded to the bucket change ownership to the bucket\n owner if the objects are uploaded with the bucket-owner-full-control canned\n ACL.

\n

ObjectWriter - The uploading account will own the object if the object is uploaded with\n the bucket-owner-full-control canned ACL.

\n

BucketOwnerEnforced - Access control lists (ACLs) are disabled and no longer affect\n permissions. The bucket owner automatically owns and has full control over every object in\n the bucket. The bucket only accepts PUT requests that don't specify an ACL or bucket owner\n full control ACLs, such as the bucket-owner-full-control canned ACL or an\n equivalent form of this ACL expressed in the XML format.

" } }, "com.amazonaws.s3#ObjectPart": { @@ -29111,6 +31179,12 @@ "traits": { "smithy.api#enumValue": "GLACIER_IR" } + }, + "SNOW": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SNOW" + } } } }, @@ -29239,7 +31313,7 @@ "DisplayName": { "target": "com.amazonaws.s3#DisplayName", "traits": { - "smithy.api#documentation": "

Container for the display name of the owner.

" + "smithy.api#documentation": "

Container for the display name of the owner. This value is only supported in the\n following Amazon Web Services Regions:

\n
    \n
  • \n

    US East (N. Virginia)

    \n
  • \n
  • \n

    US West (N. California)

    \n
  • \n
  • \n

    US West (Oregon)

    \n
  • \n
  • \n

    Asia Pacific (Singapore)

    \n
  • \n
  • \n

    Asia Pacific (Sydney)

    \n
  • \n
  • \n

    Asia Pacific (Tokyo)

    \n
  • \n
  • \n

    Europe (Ireland)

    \n
  • \n
  • \n

    South America (São Paulo)

    \n
  • \n
" } }, "ID": { @@ -29565,7 +31639,7 @@ "target": "com.amazonaws.s3#Setting", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should restrict public bucket policies for this bucket. Setting\n this element to TRUE restricts access to this bucket to only Amazon Web Service\n principals and authorized users within this account if the bucket has a public\n policy.

\n

Enabling this setting doesn't affect previously stored bucket policies, except that\n public and cross-account access within any public bucket policy, including non-public\n delegation to specific accounts, is blocked.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should restrict public bucket policies for this bucket. Setting\n this element to TRUE restricts access to this bucket to only Amazon Web Service principals and authorized users within this account if the bucket has\n a public policy.

\n

Enabling this setting doesn't affect previously stored bucket policies, except that\n public and cross-account access within any public bucket policy, including non-public\n delegation to specific accounts, is blocked.

", "smithy.api#xmlName": "RestrictPublicBuckets" } } @@ -29586,7 +31660,7 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Sets the accelerate configuration of an existing bucket. Amazon S3 Transfer Acceleration is a\n bucket-level feature that enables you to perform faster data transfers to Amazon S3.

\n

To use this operation, you must have permission to perform the\n s3:PutAccelerateConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The Transfer Acceleration state of a bucket can be set to one of the following two\n values:

\n
    \n
  • \n

    Enabled – Enables accelerated data transfers to the bucket.

    \n
  • \n
  • \n

    Suspended – Disables accelerated data transfers to the bucket.

    \n
  • \n
\n

The GetBucketAccelerateConfiguration action returns the transfer acceleration\n state of a bucket.

\n

After setting the Transfer Acceleration state of a bucket to Enabled, it might take up\n to thirty minutes before the data transfer rates to the bucket increase.

\n

The name of the bucket used for Transfer Acceleration must be DNS-compliant and must\n not contain periods (\".\").

\n

For more information about transfer acceleration, see Transfer Acceleration.

\n

The following operations are related to\n PutBucketAccelerateConfiguration:

\n ", + "smithy.api#documentation": "

Sets the accelerate configuration of an existing bucket. Amazon S3 Transfer Acceleration is a\n bucket-level feature that enables you to perform faster data transfers to Amazon S3.

\n

To use this operation, you must have permission to perform the\n s3:PutAccelerateConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The Transfer Acceleration state of a bucket can be set to one of the following two\n values:

\n
    \n
  • \n

    Enabled – Enables accelerated data transfers to the bucket.

    \n
  • \n
  • \n

    Suspended – Disables accelerated data transfers to the bucket.

    \n
  • \n
\n

The GetBucketAccelerateConfiguration action returns the transfer acceleration state\n of a bucket.

\n

After setting the Transfer Acceleration state of a bucket to Enabled, it might take up\n to thirty minutes before the data transfer rates to the bucket increase.

\n

The name of the bucket used for Transfer Acceleration must be DNS-compliant and must\n not contain periods (\".\").

\n

For more information about transfer acceleration, see Transfer\n Acceleration.

\n

The following operations are related to\n PutBucketAccelerateConfiguration:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?accelerate", @@ -29649,7 +31723,18 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the permissions on an existing bucket using access control lists (ACL). For more\n information, see Using ACLs. To set\n the ACL of a bucket, you must have WRITE_ACP permission.

\n

You can use one of the following two ways to set a bucket's permissions:

\n
    \n
  • \n

    Specify the ACL in the request body

    \n
  • \n
  • \n

    Specify permissions using request headers

    \n
  • \n
\n \n

You cannot specify access permission using both the body and the request\n headers.

\n
\n

Depending on your application needs, you may choose to set the ACL on a bucket using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, then you can continue to use that\n approach.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs are disabled and no longer affect permissions. \n You must use policies to grant access to your bucket and the objects in it. Requests to set ACLs or update ACLs fail and \n return the AccessControlListNotSupported error code. Requests to read ACLs are still supported.\n For more information, see Controlling object ownership\n in the Amazon S3 User Guide.

\n
\n

\n Access Permissions\n

\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. Specify the canned ACL name as the\n value of x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n the x-amz-acl header to set a canned ACL. These parameters map to the\n set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL)\n Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-write header grants create,\n overwrite, and delete objects permission to LogDelivery group predefined by Amazon S3 and\n two Amazon Web Services accounts identified by their email addresses.

    \n

    \n x-amz-grant-write: uri=\"http://acs.amazonaws.com/groups/s3/LogDelivery\",\n id=\"111122223333\", id=\"555566667777\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n

\n Grantee Values\n

\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>lt;/Grantee>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets the permissions on an existing bucket using access control lists (ACL). For more\n information, see Using ACLs. To set the ACL of a\n bucket, you must have WRITE_ACP permission.

\n

You can use one of the following two ways to set a bucket's permissions:

\n
    \n
  • \n

    Specify the ACL in the request body

    \n
  • \n
  • \n

    Specify permissions using request headers

    \n
  • \n
\n \n

You cannot specify access permission using both the body and the request\n headers.

\n
\n

Depending on your application needs, you may choose to set the ACL on a bucket using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, then you can continue to use that\n approach.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs\n are disabled and no longer affect permissions. You must use policies to grant access to\n your bucket and the objects in it. Requests to set ACLs or update ACLs fail and return\n the AccessControlListNotSupported error code. Requests to read ACLs are\n still supported. For more information, see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n
\n
Permissions
\n
\n

You can set access permissions by using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. Specify the canned ACL name as the\n value of x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n the x-amz-acl header to set a canned ACL. These parameters map to the\n set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control\n List (ACL) Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-write header grants create,\n overwrite, and delete objects permission to LogDelivery group predefined by Amazon S3 and\n two Amazon Web Services accounts identified by their email addresses.

    \n

    \n x-amz-grant-write: uri=\"http://acs.amazonaws.com/groups/s3/LogDelivery\",\n id=\"111122223333\", id=\"555566667777\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>&\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n
\n
\n

The following operations are related to PutBucketAcl:

\n ", + "smithy.api#examples": [ + { + "title": "Put bucket acl", + "documentation": "The following example replaces existing ACL on a bucket. The ACL grants the bucket owner (specified using the owner ID) and write permission to the LogDelivery group. Because this is a replace operation, you must specify all the grants in your request. To incrementally add or remove ACL grants, you might use the console.", + "input": { + "Bucket": "examplebucket", + "GrantFullControl": "id=examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484", + "GrantWrite": "uri=http://acs.amazonaws.com/groups/s3/LogDelivery" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?acl", @@ -29724,7 +31809,7 @@ "GrantWrite": { "target": "com.amazonaws.s3#GrantWrite", "traits": { - "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and overwrites of those objects.

", + "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and\n overwrites of those objects.

", "smithy.api#httpHeader": "x-amz-grant-write" } }, @@ -29756,7 +31841,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Sets an analytics configuration for the bucket (specified by the analytics configuration\n ID). You can have up to 1,000 analytics configurations per bucket.

\n

You can choose to have storage class analysis export analysis reports sent to a\n comma-separated values (CSV) flat file. See the DataExport request element.\n Reports are updated daily and are based on the object filters that you configure. When\n selecting data export, you specify a destination bucket and an optional destination prefix\n where the file is written. You can export the data to a destination bucket in a different\n account. However, the destination bucket must be in the same Region as the bucket that you\n are making the PUT analytics configuration to. For more information, see Amazon S3 Analytics – Storage Class\n Analysis.

\n \n

You must create a bucket policy on the destination bucket where the exported file is\n written to grant permissions to Amazon S3 to write objects to the bucket. For an example\n policy, see Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 400 Bad Request\n

      \n
    • \n
    • \n

      \n Code: InvalidArgument\n

      \n
    • \n
    • \n

      \n Cause: Invalid argument.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 400 Bad Request\n

      \n
    • \n
    • \n

      \n Code: TooManyConfigurations\n

      \n
    • \n
    • \n

      \n Cause: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 403 Forbidden\n

      \n
    • \n
    • \n

      \n Code: AccessDenied\n

      \n
    • \n
    • \n

      \n Cause: You are not the owner of the specified bucket, or you do\n not have the s3:PutAnalyticsConfiguration bucket permission to set the\n configuration on the bucket.\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets an analytics configuration for the bucket (specified by the analytics configuration\n ID). You can have up to 1,000 analytics configurations per bucket.

\n

You can choose to have storage class analysis export analysis reports sent to a\n comma-separated values (CSV) flat file. See the DataExport request element.\n Reports are updated daily and are based on the object filters that you configure. When\n selecting data export, you specify a destination bucket and an optional destination prefix\n where the file is written. You can export the data to a destination bucket in a different\n account. However, the destination bucket must be in the same Region as the bucket that you\n are making the PUT analytics configuration to. For more information, see Amazon S3\n Analytics – Storage Class Analysis.

\n \n

You must create a bucket policy on the destination bucket where the exported file is\n written to grant permissions to Amazon S3 to write objects to the bucket. For an example\n policy, see Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

\n PutBucketAnalyticsConfiguration has the following special errors:

\n
    \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 400 Bad Request\n

      \n
    • \n
    • \n

      \n Code: InvalidArgument\n

      \n
    • \n
    • \n

      \n Cause: Invalid argument.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 400 Bad Request\n

      \n
    • \n
    • \n

      \n Code: TooManyConfigurations\n

      \n
    • \n
    • \n

      \n Cause: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 403 Forbidden\n

      \n
    • \n
    • \n

      \n Code: AccessDenied\n

      \n
    • \n
    • \n

      \n Cause: You are not the owner of the specified bucket, or you do\n not have the s3:PutAnalyticsConfiguration bucket permission to set the\n configuration on the bucket.\n

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutBucketAnalyticsConfiguration:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?analytics", @@ -29820,7 +31905,50 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the cors configuration for your bucket. If the configuration exists,\n Amazon S3 replaces it.

\n

To use this operation, you must be allowed to perform the s3:PutBucketCORS\n action. By default, the bucket owner has this permission and can grant it to others.

\n

You set this configuration on a bucket so that the bucket can service cross-origin\n requests. For example, you might want to enable a request whose origin is\n http://www.example.com to access your Amazon S3 bucket at\n my.example.bucket.com by using the browser's XMLHttpRequest\n capability.

\n

To enable cross-origin resource sharing (CORS) on a bucket, you add the\n cors subresource to the bucket. The cors subresource is an XML\n document in which you configure rules that identify origins and the HTTP methods that can\n be executed on your bucket. The document is limited to 64 KB in size.

\n

When Amazon S3 receives a cross-origin request (or a pre-flight OPTIONS request) against a\n bucket, it evaluates the cors configuration on the bucket and uses the first\n CORSRule rule that matches the incoming browser request to enable a\n cross-origin request. For a rule to match, the following conditions must be met:

\n
    \n
  • \n

    The request's Origin header must match AllowedOrigin\n elements.

    \n
  • \n
  • \n

    The request method (for example, GET, PUT, HEAD, and so on) or the\n Access-Control-Request-Method header in case of a pre-flight\n OPTIONS request must be one of the AllowedMethod\n elements.

    \n
  • \n
  • \n

    Every header specified in the Access-Control-Request-Headers request\n header of a pre-flight request must match an AllowedHeader element.\n

    \n
  • \n
\n

For more information about CORS, go to Enabling\n Cross-Origin Resource Sharing in the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets the cors configuration for your bucket. If the configuration exists,\n Amazon S3 replaces it.

\n

To use this operation, you must be allowed to perform the s3:PutBucketCORS\n action. By default, the bucket owner has this permission and can grant it to others.

\n

You set this configuration on a bucket so that the bucket can service cross-origin\n requests. For example, you might want to enable a request whose origin is\n http://www.example.com to access your Amazon S3 bucket at\n my.example.bucket.com by using the browser's XMLHttpRequest\n capability.

\n

To enable cross-origin resource sharing (CORS) on a bucket, you add the\n cors subresource to the bucket. The cors subresource is an XML\n document in which you configure rules that identify origins and the HTTP methods that can\n be executed on your bucket. The document is limited to 64 KB in size.

\n

When Amazon S3 receives a cross-origin request (or a pre-flight OPTIONS request) against a\n bucket, it evaluates the cors configuration on the bucket and uses the first\n CORSRule rule that matches the incoming browser request to enable a\n cross-origin request. For a rule to match, the following conditions must be met:

\n
    \n
  • \n

    The request's Origin header must match AllowedOrigin\n elements.

    \n
  • \n
  • \n

    The request method (for example, GET, PUT, HEAD, and so on) or the\n Access-Control-Request-Method header in case of a pre-flight\n OPTIONS request must be one of the AllowedMethod\n elements.

    \n
  • \n
  • \n

    Every header specified in the Access-Control-Request-Headers request\n header of a pre-flight request must match an AllowedHeader element.\n

    \n
  • \n
\n

For more information about CORS, go to Enabling Cross-Origin Resource Sharing in\n the Amazon S3 User Guide.

\n

The following operations are related to PutBucketCors:

\n ", + "smithy.api#examples": [ + { + "title": "To set cors configuration on a bucket.", + "documentation": "The following example enables PUT, POST, and DELETE requests from www.example.com, and enables GET requests from any domain.", + "input": { + "Bucket": "", + "CORSConfiguration": { + "CORSRules": [ + { + "AllowedOrigins": [ + "http://www.example.com" + ], + "AllowedHeaders": [ + "*" + ], + "AllowedMethods": [ + "PUT", + "POST", + "DELETE" + ], + "MaxAgeSeconds": 3000, + "ExposeHeaders": [ + "x-amz-server-side-encryption" + ] + }, + { + "AllowedOrigins": [ + "*" + ], + "AllowedHeaders": [ + "Authorization" + ], + "AllowedMethods": [ + "GET" + ], + "MaxAgeSeconds": 3000 + } + ] + }, + "ContentMD5": "" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?cors", @@ -29845,7 +31973,7 @@ "CORSConfiguration": { "target": "com.amazonaws.s3#CORSConfiguration", "traits": { - "smithy.api#documentation": "

Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more\n information, see Enabling Cross-Origin Resource\n Sharing in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more\n information, see Enabling\n Cross-Origin Resource Sharing in the\n Amazon S3 User Guide.

", "smithy.api#httpPayload": {}, "smithy.api#required": {}, "smithy.api#xmlName": "CORSConfiguration" @@ -29890,7 +32018,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

This action uses the encryption subresource to configure default\n encryption and Amazon S3 Bucket Key for an existing bucket.

\n

Default encryption for a bucket can use server-side encryption with Amazon S3-managed keys\n (SSE-S3) or customer managed keys (SSE-KMS). If you specify default encryption\n using SSE-KMS, you can also configure Amazon S3 Bucket Key. When the default encryption is SSE-KMS, if\n you upload an object to the bucket and do not specify the KMS key to use for encryption, Amazon S3\n uses the default Amazon Web Services managed KMS key for your account. For information about default\n encryption, see Amazon S3 default bucket encryption\n in the Amazon S3 User Guide. For more information about S3 Bucket Keys,\n see Amazon S3 Bucket Keys in the Amazon S3 User Guide.

\n \n

This action requires Amazon Web Services Signature Version 4. For more information, see Authenticating Requests (Amazon Web Services Signature\n Version 4).

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This action uses the encryption subresource to configure default encryption\n and Amazon S3 Bucket Keys for an existing bucket.

\n

By default, all buckets have a default encryption configuration that uses server-side\n encryption with Amazon S3 managed keys (SSE-S3). You can optionally configure default encryption\n for a bucket by using server-side encryption with Key Management Service (KMS) keys (SSE-KMS),\n dual-layer server-side encryption with Amazon Web Services KMS keys (DSSE-KMS), or server-side\n encryption with customer-provided keys (SSE-C). If you specify default encryption by using\n SSE-KMS, you can also configure Amazon S3 Bucket Keys. For information about bucket default\n encryption, see Amazon S3 bucket default encryption\n in the Amazon S3 User Guide. For more information about S3 Bucket Keys, see\n Amazon S3 Bucket\n Keys in the Amazon S3 User Guide.

\n \n

This action requires Amazon Web Services Signature Version 4. For more information, see \n Authenticating Requests (Amazon Web Services Signature Version 4).

\n
\n

To use this operation, you must have permission to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

The following operations are related to PutBucketEncryption:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?encryption", @@ -29904,7 +32032,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Specifies default encryption for a bucket using server-side encryption with Amazon S3-managed\n keys (SSE-S3) or customer managed keys (SSE-KMS). For information about\n the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption\n in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies default encryption for a bucket using server-side encryption with different\n key options. By default, all buckets have a default encryption configuration that uses\n server-side encryption with Amazon S3 managed keys (SSE-S3). You can optionally configure\n default encryption for a bucket by using server-side encryption with an Amazon Web Services KMS key\n (SSE-KMS) or a customer-provided key (SSE-C). For information about the bucket default\n encryption feature, see Amazon S3 Bucket Default Encryption\n in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -29915,7 +32043,7 @@ "ContentMD5": { "target": "com.amazonaws.s3#ContentMD5", "traits": { - "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the server-side encryption configuration.

\n

For requests made using the Amazon Web Services Command Line Interface (CLI) or Amazon Web Services SDKs, this field is calculated automatically.

", + "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the server-side encryption\n configuration.

\n

For requests made using the Amazon Web Services Command Line Interface (CLI) or Amazon Web Services SDKs, this field is calculated automatically.

", "smithy.api#httpHeader": "Content-MD5" } }, @@ -29955,7 +32083,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Puts a S3 Intelligent-Tiering configuration to the specified bucket.\n You can have up to 1,000 S3 Intelligent-Tiering configurations per bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to\n PutBucketIntelligentTieringConfiguration include:

\n \n \n

You only need S3 Intelligent-Tiering enabled on a bucket if you want to automatically\n move objects stored in the S3 Intelligent-Tiering storage class to the\n Archive Access or Deep Archive Access tier.

\n
\n

\n Special Errors\n

\n
    \n
  • \n

    \n HTTP 400 Bad Request Error\n

    \n
      \n
    • \n

      \n Code: InvalidArgument

      \n
    • \n
    • \n

      \n Cause: Invalid Argument

      \n
    • \n
    \n
  • \n
  • \n

    \n HTTP 400 Bad Request Error\n

    \n
      \n
    • \n

      \n Code: TooManyConfigurations

      \n
    • \n
    • \n

      \n Cause: You are attempting to create a new configuration\n but have already reached the 1,000-configuration limit.

      \n
    • \n
    \n
  • \n
  • \n

    \n HTTP 403 Forbidden Error\n

    \n
      \n
    • \n

      \n Code: AccessDenied

      \n
    • \n
    • \n

      \n Cause: You are not the owner of the specified bucket,\n or you do not have the s3:PutIntelligentTieringConfiguration bucket\n permission to set the configuration on the bucket.

      \n
    • \n
    \n
  • \n
", + "smithy.api#documentation": "

Puts a S3 Intelligent-Tiering configuration to the specified bucket. You can have up to\n 1,000 S3 Intelligent-Tiering configurations per bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to PutBucketIntelligentTieringConfiguration include:

\n \n \n

You only need S3 Intelligent-Tiering enabled on a bucket if you want to automatically\n move objects stored in the S3 Intelligent-Tiering storage class to the Archive Access\n or Deep Archive Access tier.

\n
\n

\n PutBucketIntelligentTieringConfiguration has the following special errors:

\n
\n
HTTP 400 Bad Request Error
\n
\n

\n Code: InvalidArgument

\n

\n Cause: Invalid Argument

\n
\n
HTTP 400 Bad Request Error
\n
\n

\n Code: TooManyConfigurations

\n

\n Cause: You are attempting to create a new configuration\n but have already reached the 1,000-configuration limit.

\n
\n
HTTP 403 Forbidden Error
\n
\n

\n Cause: You are not the owner of the specified bucket,\n or you do not have the s3:PutIntelligentTieringConfiguration\n bucket permission to set the configuration on the bucket.

\n
\n
", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?intelligent-tiering", @@ -30008,7 +32136,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

This implementation of the PUT action adds an inventory configuration\n (identified by the inventory ID) to the bucket. You can have up to 1,000 inventory\n configurations per bucket.

\n

Amazon S3 inventory generates inventories of the objects in the bucket on a daily or weekly\n basis, and the results are published to a flat file. The bucket that is inventoried is\n called the source bucket, and the bucket where the inventory flat file\n is stored is called the destination bucket. The\n destination bucket must be in the same Amazon Web Services Region as the\n source bucket.

\n

When you configure an inventory for a source bucket, you specify\n the destination bucket where you want the inventory to be stored, and\n whether to generate the inventory daily or weekly. You can also configure what object\n metadata to include and whether to inventory all object versions or only current versions.\n For more information, see Amazon S3\n Inventory in the Amazon S3 User Guide.

\n \n

You must create a bucket policy on the destination bucket to\n grant permissions to Amazon S3 to write objects to the bucket in the defined location. For an\n example policy, see \n Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutInventoryConfiguration action. The bucket owner has this permission\n by default and can grant this permission to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

\n Special Errors\n

\n
    \n
  • \n

    \n HTTP 400 Bad Request Error\n

    \n
      \n
    • \n

      \n Code: InvalidArgument

      \n
    • \n
    • \n

      \n Cause: Invalid Argument

      \n
    • \n
    \n
  • \n
  • \n

    \n HTTP 400 Bad Request Error\n

    \n
      \n
    • \n

      \n Code: TooManyConfigurations

      \n
    • \n
    • \n

      \n Cause: You are attempting to create a new configuration\n but have already reached the 1,000-configuration limit.

      \n
    • \n
    \n
  • \n
  • \n

    \n HTTP 403 Forbidden Error\n

    \n
      \n
    • \n

      \n Code: AccessDenied

      \n
    • \n
    • \n

      \n Cause: You are not the owner of the specified bucket,\n or you do not have the s3:PutInventoryConfiguration bucket\n permission to set the configuration on the bucket.

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the PUT action adds an inventory configuration\n (identified by the inventory ID) to the bucket. You can have up to 1,000 inventory\n configurations per bucket.

\n

Amazon S3 inventory generates inventories of the objects in the bucket on a daily or weekly\n basis, and the results are published to a flat file. The bucket that is inventoried is\n called the source bucket, and the bucket where the inventory flat file\n is stored is called the destination bucket. The\n destination bucket must be in the same Amazon Web Services Region as the\n source bucket.

\n

When you configure an inventory for a source bucket, you specify\n the destination bucket where you want the inventory to be stored, and\n whether to generate the inventory daily or weekly. You can also configure what object\n metadata to include and whether to inventory all object versions or only current versions.\n For more information, see Amazon S3 Inventory in the\n Amazon S3 User Guide.

\n \n

You must create a bucket policy on the destination bucket to\n grant permissions to Amazon S3 to write objects to the bucket in the defined location. For an\n example policy, see Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

\n
\n
\n
Permissions
\n
\n

To use this operation, you must have permission to perform the\n s3:PutInventoryConfiguration action. The bucket owner has this permission\n by default and can grant this permission to others.

\n

The s3:PutInventoryConfiguration permission allows a user to create an\n S3\n Inventory report that includes all object metadata fields available and to\n specify the destination bucket to store the inventory. A user with read access to objects\n in the destination bucket can also access all object metadata fields that are available in\n the inventory report.

\n

To restrict access to an inventory report, see Restricting access to an Amazon S3 Inventory report in the\n Amazon S3 User Guide. For more information about the metadata fields\n available in S3 Inventory, see Amazon S3\n Inventory lists in the Amazon S3 User Guide. For more\n information about permissions, see Permissions related to bucket subresource operations and Identity and\n access management in Amazon S3 in the Amazon S3 User Guide.

\n
\n
\n

\n PutBucketInventoryConfiguration has the following special errors:

\n
\n
HTTP 400 Bad Request Error
\n
\n

\n Code: InvalidArgument

\n

\n Cause: Invalid Argument

\n
\n
HTTP 400 Bad Request Error
\n
\n

\n Code: TooManyConfigurations

\n

\n Cause: You are attempting to create a new configuration\n but have already reached the 1,000-configuration limit.

\n
\n
HTTP 403 Forbidden Error
\n
\n

\n Cause: You are not the owner of the specified bucket,\n or you do not have the s3:PutInventoryConfiguration bucket\n permission to set the configuration on the bucket.

\n
\n
\n

The following operations are related to PutBucketInventoryConfiguration:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?inventory", @@ -30072,7 +32200,36 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle\n configuration. Keep in mind that this will overwrite an existing lifecycle configuration, so if\n you want to retain any configuration details, they must be included in the new lifecycle\n configuration. For information about lifecycle configuration, see Managing your storage\n lifecycle.

\n \n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The previous version of the API supported\n filtering based only on an object key name prefix, which is supported for backward\n compatibility. For the related API description, see PutBucketLifecycle.

\n
\n

\n Rules\n

\n

You specify the lifecycle configuration in your request body. The lifecycle\n configuration is specified as XML consisting of one or more rules. An Amazon S3 Lifecycle\n configuration can have up to 1,000 rules. This limit is not adjustable. Each rule consists\n of the following:

\n
    \n
  • \n

    Filter identifying a subset of objects to which the rule applies. The filter can\n be based on a key name prefix, object tags, or a combination of both.

    \n
  • \n
  • \n

    Status whether the rule is in effect.

    \n
  • \n
  • \n

    One or more lifecycle transition and expiration actions that you want Amazon S3 to\n perform on the objects identified by the filter. If the state of your bucket is\n versioning-enabled or versioning-suspended, you can have many versions of the same\n object (one current version and zero or more noncurrent versions). Amazon S3 provides\n predefined actions that you can specify for current and noncurrent object\n versions.

    \n
  • \n
\n

For more information, see Object\n Lifecycle Management and Lifecycle Configuration Elements.

\n

\n Permissions\n

\n

By default, all Amazon S3 resources are private, including buckets, objects, and related\n subresources (for example, lifecycle configuration and website configuration). Only the\n resource owner (that is, the Amazon Web Services account that created it) can access the resource. The\n resource owner can optionally grant access permissions to others by writing an access\n policy. For this operation, a user must get the s3:PutLifecycleConfiguration\n permission.

\n

You can also explicitly deny permissions. Explicit deny also supersedes any other\n permissions. If you want to block users or accounts from removing or deleting objects from\n your bucket, you must deny them permissions for the following actions:

\n
    \n
  • \n

    \n s3:DeleteObject\n

    \n
  • \n
  • \n

    \n s3:DeleteObjectVersion\n

    \n
  • \n
  • \n

    \n s3:PutLifecycleConfiguration\n

    \n
  • \n
\n

For more information about permissions, see Managing Access Permissions to Your Amazon S3\n Resources.

\n

The following are related to PutBucketLifecycleConfiguration:

\n ", + "smithy.api#documentation": "

Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle\n configuration. Keep in mind that this will overwrite an existing lifecycle configuration,\n so if you want to retain any configuration details, they must be included in the new\n lifecycle configuration. For information about lifecycle configuration, see Managing\n your storage lifecycle.

\n \n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The previous version of the API supported\n filtering based only on an object key name prefix, which is supported for backward\n compatibility. For the related API description, see PutBucketLifecycle.

\n
\n
\n
Rules
\n
\n

You specify the lifecycle configuration in your request body. The lifecycle\n configuration is specified as XML consisting of one or more rules. An Amazon S3 Lifecycle\n configuration can have up to 1,000 rules. This limit is not adjustable. Each rule consists\n of the following:

\n
    \n
  • \n

    A filter identifying a subset of objects to which the rule applies. The filter can\n be based on a key name prefix, object tags, or a combination of both.

    \n
  • \n
  • \n

    A status indicating whether the rule is in effect.

    \n
  • \n
  • \n

    One or more lifecycle transition and expiration actions that you want Amazon S3 to\n perform on the objects identified by the filter. If the state of your bucket is\n versioning-enabled or versioning-suspended, you can have many versions of the same\n object (one current version and zero or more noncurrent versions). Amazon S3 provides\n predefined actions that you can specify for current and noncurrent object\n versions.

    \n
  • \n
\n

For more information, see Object Lifecycle Management\n and Lifecycle Configuration Elements.

\n
\n
Permissions
\n
\n

By default, all Amazon S3 resources are private, including buckets, objects, and related\n subresources (for example, lifecycle configuration and website configuration). Only the\n resource owner (that is, the Amazon Web Services account that created it) can access the resource. The\n resource owner can optionally grant access permissions to others by writing an access\n policy. For this operation, a user must get the s3:PutLifecycleConfiguration\n permission.

\n

You can also explicitly deny permissions. An explicit deny also supersedes any other\n permissions. If you want to block users or accounts from removing or deleting objects from\n your bucket, you must deny them permissions for the following actions:

\n
    \n
  • \n

    \n s3:DeleteObject\n

    \n
  • \n
  • \n

    \n s3:DeleteObjectVersion\n

    \n
  • \n
  • \n

    \n s3:PutLifecycleConfiguration\n

    \n
  • \n
\n

For more information about permissions, see Managing Access Permissions to\n Your Amazon S3 Resources.

\n
\n
\n

The following operations are related to PutBucketLifecycleConfiguration:

\n ", + "smithy.api#examples": [ + { + "title": "Put bucket lifecycle", + "documentation": "The following example replaces existing lifecycle configuration, if any, on the specified bucket. ", + "input": { + "Bucket": "examplebucket", + "LifecycleConfiguration": { + "Rules": [ + { + "Filter": { + "Prefix": "documents/" + }, + "Status": "Enabled", + "Transitions": [ + { + "Days": 365, + "StorageClass": "GLACIER" + } + ], + "Expiration": { + "Days": 3650 + }, + "ID": "TestOnly" + } + ] + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?lifecycle", @@ -30134,7 +32291,31 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Set the logging parameters for a bucket and to specify permissions for who can view and\n modify the logging parameters. All logs are saved to buckets in the same Amazon Web Services Region as the\n source bucket. To set the logging status of a bucket, you must be the bucket owner.

\n

The bucket owner is automatically granted FULL_CONTROL to all logs. You use the Grantee request element to grant access to other people. The\n Permissions request element specifies the kind of access the grantee has to\n the logs.

\n \n

If the target bucket for log delivery uses the bucket owner enforced\n setting for S3 Object Ownership, you can't use the Grantee request element\n to grant access to others. Permissions can only be granted using policies. For more information, see Permissions for server access log delivery in the\n Amazon S3 User Guide.

\n
\n

\n Grantee Values\n

\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
\n

To enable logging, you use LoggingEnabled and its children request elements. To disable\n logging, you use an empty BucketLoggingStatus request element:

\n

\n \n

\n

For more information about server access logging, see Server Access Logging in the Amazon S3 User Guide.

\n

For more information about creating a bucket, see CreateBucket. For more\n information about returning the logging status of a bucket, see GetBucketLogging.

\n

The following operations are related to PutBucketLogging:

\n ", + "smithy.api#documentation": "

Set the logging parameters for a bucket and to specify permissions for who can view and\n modify the logging parameters. All logs are saved to buckets in the same Amazon Web Services Region as\n the source bucket. To set the logging status of a bucket, you must be the bucket\n owner.

\n

The bucket owner is automatically granted FULL_CONTROL to all logs. You use the\n Grantee request element to grant access to other people. The\n Permissions request element specifies the kind of access the grantee has to\n the logs.

\n \n

If the target bucket for log delivery uses the bucket owner enforced setting for S3\n Object Ownership, you can't use the Grantee request element to grant access\n to others. Permissions can only be granted using policies. For more information, see\n Permissions for server access log delivery in the\n Amazon S3 User Guide.

\n
\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (by using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    \n DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GETObjectAcl\n request, appears as the CanonicalUser.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
\n
\n
\n

To enable logging, you use LoggingEnabled and its children request elements. To disable\n logging, you use an empty BucketLoggingStatus request element:

\n

\n \n

\n

For more information about server access logging, see Server Access Logging in the\n Amazon S3 User Guide.

\n

For more information about creating a bucket, see CreateBucket. For more\n information about returning the logging status of a bucket, see GetBucketLogging.

\n

The following operations are related to PutBucketLogging:

\n ", + "smithy.api#examples": [ + { + "title": "Set logging configuration for a bucket", + "documentation": "The following example sets logging policy on a bucket. For the Log Delivery group to deliver logs to the destination bucket, it needs permission for the READ_ACP action which the policy grants.", + "input": { + "Bucket": "sourcebucket", + "BucketLoggingStatus": { + "LoggingEnabled": { + "TargetBucket": "targetbucket", + "TargetPrefix": "MyBucketLogs/", + "TargetGrants": [ + { + "Grantee": { + "Type": "Group", + "URI": "http://acs.amazonaws.com/groups/global/AllUsers" + }, + "Permission": "READ" + } + ] + } + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?logging", @@ -30200,7 +32381,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Sets a metrics configuration (specified by the metrics configuration ID) for the bucket.\n You can have up to 1,000 metrics configurations per bucket. If you're updating an existing\n metrics configuration, note that this is a full replacement of the existing metrics\n configuration. If you don't include the elements you want to keep, they are erased.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon\n CloudWatch.

\n

The following operations are related to\n PutBucketMetricsConfiguration:

\n \n

\n GetBucketLifecycle has the following special error:

\n
    \n
  • \n

    Error code: TooManyConfigurations\n

    \n
      \n
    • \n

      Description: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.

      \n
    • \n
    • \n

      HTTP Status Code: HTTP 400 Bad Request

      \n
    • \n
    \n
  • \n
", + "smithy.api#documentation": "

Sets a metrics configuration (specified by the metrics configuration ID) for the bucket.\n You can have up to 1,000 metrics configurations per bucket. If you're updating an existing\n metrics configuration, note that this is a full replacement of the existing metrics\n configuration. If you don't include the elements you want to keep, they are erased.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring\n Metrics with Amazon CloudWatch.

\n

The following operations are related to\n PutBucketMetricsConfiguration:

\n \n

\n PutBucketMetricsConfiguration has the following special error:

\n
    \n
  • \n

    Error code: TooManyConfigurations\n

    \n
      \n
    • \n

      Description: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.

      \n
    • \n
    • \n

      HTTP Status Code: HTTP 400 Bad Request

      \n
    • \n
    \n
  • \n
", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?metrics", @@ -30225,7 +32406,7 @@ "Id": { "target": "com.amazonaws.s3#MetricsId", "traits": { - "smithy.api#documentation": "

The ID used to identify the metrics configuration.

", + "smithy.api#documentation": "

The ID used to identify the metrics configuration. The ID has a 64 character limit and\n can only contain letters, numbers, periods, dashes, and underscores.

", "smithy.api#httpQuery": "id", "smithy.api#required": {} } @@ -30260,7 +32441,26 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Enables notifications of specified events for a bucket. For more information about event\n notifications, see Configuring Event\n Notifications.

\n

Using this API, you can replace an existing notification configuration. The\n configuration is an XML file that defines the event types that you want Amazon S3 to publish and\n the destination where you want Amazon S3 to publish an event notification when it detects an\n event of the specified type.

\n

By default, your bucket has no event notifications configured. That is, the notification\n configuration will be an empty NotificationConfiguration.

\n

\n \n

\n

\n \n

\n

This action replaces the existing notification configuration with the configuration\n you include in the request body.

\n

After Amazon S3 receives this request, it first verifies that any Amazon Simple Notification\n Service (Amazon SNS) or Amazon Simple Queue Service (Amazon SQS) destination exists, and\n that the bucket owner has permission to publish to it by sending a test notification. In\n the case of Lambda destinations, Amazon S3 verifies that the Lambda function permissions\n grant Amazon S3 permission to invoke the function from the Amazon S3 bucket. For more information,\n see Configuring Notifications for Amazon S3\n Events.

\n

You can disable notifications by adding the empty NotificationConfiguration\n element.

\n

For more information about the number of event notification configurations that you can create per bucket, see\n Amazon S3 service quotas in Amazon Web Services General Reference.

\n

By default, only the bucket owner can configure notifications on a bucket. However,\n bucket owners can use a bucket policy to grant permission to other users to set this\n configuration with s3:PutBucketNotification permission.

\n \n

The PUT notification is an atomic operation. For example, suppose your notification\n configuration includes SNS topic, SQS queue, and Lambda function configurations. When\n you send a PUT request with this configuration, Amazon S3 sends test messages to your SNS\n topic. If the message fails, the entire PUT action will fail, and Amazon S3 will not add\n the configuration to your bucket.

\n
\n

\n Responses\n

\n

If the configuration in the request body includes only one\n TopicConfiguration specifying only the\n s3:ReducedRedundancyLostObject event type, the response will also include\n the x-amz-sns-test-message-id header containing the message ID of the test\n notification sent to the topic.

\n

The following action is related to\n PutBucketNotificationConfiguration:

\n ", + "smithy.api#documentation": "

Enables notifications of specified events for a bucket. For more information about event\n notifications, see Configuring Event\n Notifications.

\n

Using this API, you can replace an existing notification configuration. The\n configuration is an XML file that defines the event types that you want Amazon S3 to publish and\n the destination where you want Amazon S3 to publish an event notification when it detects an\n event of the specified type.

\n

By default, your bucket has no event notifications configured. That is, the notification\n configuration will be an empty NotificationConfiguration.

\n

\n \n

\n

\n \n

\n

This action replaces the existing notification configuration with the configuration you\n include in the request body.

\n

After Amazon S3 receives this request, it first verifies that any Amazon Simple Notification\n Service (Amazon SNS) or Amazon Simple Queue Service (Amazon SQS) destination exists, and\n that the bucket owner has permission to publish to it by sending a test notification. In\n the case of Lambda destinations, Amazon S3 verifies that the Lambda function permissions\n grant Amazon S3 permission to invoke the function from the Amazon S3 bucket. For more information,\n see Configuring Notifications for Amazon S3 Events.

\n

You can disable notifications by adding the empty NotificationConfiguration\n element.

\n

For more information about the number of event notification configurations that you can\n create per bucket, see Amazon S3 service quotas in Amazon Web Services\n General Reference.

\n

By default, only the bucket owner can configure notifications on a bucket. However,\n bucket owners can use a bucket policy to grant permission to other users to set this\n configuration with the required s3:PutBucketNotification permission.

\n \n

The PUT notification is an atomic operation. For example, suppose your notification\n configuration includes SNS topic, SQS queue, and Lambda function configurations. When\n you send a PUT request with this configuration, Amazon S3 sends test messages to your SNS\n topic. If the message fails, the entire PUT action will fail, and Amazon S3 will not add the\n configuration to your bucket.

\n
\n

If the configuration in the request body includes only one\n TopicConfiguration specifying only the\n s3:ReducedRedundancyLostObject event type, the response will also include\n the x-amz-sns-test-message-id header containing the message ID of the test\n notification sent to the topic.

\n

The following action is related to\n PutBucketNotificationConfiguration:

\n ", + "smithy.api#examples": [ + { + "title": "Set notification configuration for a bucket", + "documentation": "The following example sets notification configuration on a bucket to publish the object created events to an SNS topic.", + "input": { + "Bucket": "examplebucket", + "NotificationConfiguration": { + "TopicConfigurations": [ + { + "TopicArn": "arn:aws:sns:us-west-2:123456789012:s3-notification-topic", + "Events": [ + "s3:ObjectCreated:*" + ] + } + ] + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?notification", @@ -30301,7 +32501,7 @@ "target": "com.amazonaws.s3#SkipValidation", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Skips validation of Amazon SQS, Amazon SNS, and Lambda destinations. True or false value.

", + "smithy.api#documentation": "

Skips validation of Amazon SQS, Amazon SNS, and Lambda\n destinations. True or false value.

", "smithy.api#httpHeader": "x-amz-skip-destination-validation" } } @@ -30322,7 +32522,7 @@ "aws.protocols#httpChecksum": { "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates or modifies OwnershipControls for an Amazon S3 bucket. To use this\n operation, you must have the s3:PutBucketOwnershipControls permission. For\n more information about Amazon S3 permissions, see Specifying permissions in a policy.

\n

For information about Amazon S3 Object Ownership, see Using object ownership.

\n

The following operations are related to PutBucketOwnershipControls:

\n ", + "smithy.api#documentation": "

Creates or modifies OwnershipControls for an Amazon S3 bucket. To use this\n operation, you must have the s3:PutBucketOwnershipControls permission. For\n more information about Amazon S3 permissions, see Specifying permissions in a\n policy.

\n

For information about Amazon S3 Object Ownership, see Using object\n ownership.

\n

The following operations are related to PutBucketOwnershipControls:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?ownershipControls", @@ -30361,7 +32561,7 @@ "OwnershipControls": { "target": "com.amazonaws.s3#OwnershipControls", "traits": { - "smithy.api#documentation": "

The OwnershipControls (BucketOwnerEnforced, BucketOwnerPreferred, or ObjectWriter) that you want\n to apply to this Amazon S3 bucket.

", + "smithy.api#documentation": "

The OwnershipControls (BucketOwnerEnforced, BucketOwnerPreferred, or\n ObjectWriter) that you want to apply to this Amazon S3 bucket.

", "smithy.api#httpPayload": {}, "smithy.api#required": {}, "smithy.api#xmlName": "OwnershipControls" @@ -30385,7 +32585,17 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Applies an Amazon S3 bucket policy to an Amazon S3 bucket. If you are using an identity other than\n the root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n PutBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have PutBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

As a security precaution, the root user of the Amazon Web Services account that owns a bucket can\n always use this operation, even if the policy explicitly denies the root user the\n ability to perform this action.

\n
\n

For more information, see Bucket policy examples.

\n

The following operations are related to PutBucketPolicy:

\n ", + "smithy.api#documentation": "

Applies an Amazon S3 bucket policy to an Amazon S3 bucket. If you are using an identity other than\n the root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n PutBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have PutBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

For more information, see Bucket policy\n examples.

\n

The following operations are related to PutBucketPolicy:

\n ", + "smithy.api#examples": [ + { + "title": "Set bucket policy", + "documentation": "The following example sets a permission policy on a bucket.", + "input": { + "Bucket": "examplebucket", + "Policy": "{\"Version\": \"2012-10-17\", \"Statement\": [{ \"Sid\": \"id-1\",\"Effect\": \"Allow\",\"Principal\": {\"AWS\": \"arn:aws:iam::123456789012:root\"}, \"Action\": [ \"s3:PutObject\",\"s3:PutObjectAcl\"], \"Resource\": [\"arn:aws:s3:::acl3/*\" ] } ]}" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?policy", @@ -30462,7 +32672,29 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates a replication configuration or replaces an existing one. For more information,\n see Replication in the Amazon S3 User Guide.

\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the name of the destination bucket or buckets where you want\n Amazon S3 to replicate objects, the IAM role that Amazon S3 can assume to replicate objects on your\n behalf, and other relevant information.

\n

A replication configuration must include at least one rule, and can contain a maximum of\n 1,000. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source bucket. To choose additional subsets of objects to replicate, add a rule for\n each subset.

\n

To specify a subset of the objects in the source bucket to apply a replication rule to,\n add the Filter element as a child of the Rule element. You can filter objects based on an\n object key prefix, one or more object tags, or both. When you add the Filter element in the\n configuration, you must also add the following elements:\n DeleteMarkerReplication, Status, and\n Priority.

\n \n

If you are using an earlier version of the replication configuration, Amazon S3 handles\n replication of delete markers differently. For more information, see Backward Compatibility.

\n
\n

For information about enabling versioning on a bucket, see Using Versioning.

\n

\n Handling Replication of Encrypted Objects\n

\n

By default, Amazon S3 doesn't replicate objects that are stored at rest using server-side\n encryption with KMS keys. To replicate Amazon Web Services KMS-encrypted objects, add the\n following: SourceSelectionCriteria, SseKmsEncryptedObjects,\n Status, EncryptionConfiguration, and\n ReplicaKmsKeyID. For information about replication configuration, see\n Replicating Objects\n Created with SSE Using KMS keys.

\n

For information on PutBucketReplication errors, see List of\n replication-related error codes\n

\n

\n Permissions\n

\n

To create a PutBucketReplication request, you must have s3:PutReplicationConfiguration \n permissions for the bucket. \n

\n

By default, a resource owner, in this case the Amazon Web Services account that created the bucket, can\n perform this operation. The resource owner can also grant others permissions to perform the\n operation. For more information about permissions, see Specifying Permissions in a Policy\n and Managing Access Permissions to Your\n Amazon S3 Resources.

\n \n

To perform this operation, the user or role performing the action must have the\n iam:PassRole permission.

\n
\n

The following operations are related to PutBucketReplication:

\n ", + "smithy.api#documentation": "

Creates a replication configuration or replaces an existing one. For more information,\n see Replication in the Amazon S3 User Guide.

\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the name of the destination bucket or buckets where you want\n Amazon S3 to replicate objects, the IAM role that Amazon S3 can assume to replicate objects on your\n behalf, and other relevant information.

\n

A replication configuration must include at least one rule, and can contain a maximum of\n 1,000. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source bucket. To choose additional subsets of objects to replicate, add a rule for\n each subset.

\n

To specify a subset of the objects in the source bucket to apply a replication rule to,\n add the Filter element as a child of the Rule element. You can filter objects based on an\n object key prefix, one or more object tags, or both. When you add the Filter element in the\n configuration, you must also add the following elements:\n DeleteMarkerReplication, Status, and\n Priority.

\n \n

If you are using an earlier version of the replication configuration, Amazon S3 handles\n replication of delete markers differently. For more information, see Backward Compatibility.

\n
\n

For information about enabling versioning on a bucket, see Using Versioning.

\n
\n
Handling Replication of Encrypted Objects
\n
\n

By default, Amazon S3 doesn't replicate objects that are stored at rest using server-side\n encryption with KMS keys. To replicate Amazon Web Services KMS-encrypted objects, add the following:\n SourceSelectionCriteria, SseKmsEncryptedObjects,\n Status, EncryptionConfiguration, and\n ReplicaKmsKeyID. For information about replication configuration, see\n Replicating Objects\n Created with SSE Using KMS keys.

\n

For information on PutBucketReplication errors, see List of\n replication-related error codes\n

\n
\n
Permissions
\n
\n

To create a PutBucketReplication request, you must have\n s3:PutReplicationConfiguration permissions for the bucket.\n \n

\n

By default, a resource owner, in this case the Amazon Web Services account that created the bucket,\n can perform this operation. The resource owner can also grant others permissions to perform\n the operation. For more information about permissions, see Specifying Permissions in a\n Policy and Managing Access Permissions to\n Your Amazon S3 Resources.

\n \n

To perform this operation, the user or role performing the action must have the\n iam:PassRole permission.

\n
\n
\n
\n

The following operations are related to PutBucketReplication:

\n ", + "smithy.api#examples": [ + { + "title": "Set replication configuration on a bucket", + "documentation": "The following example sets replication configuration on a bucket.", + "input": { + "Bucket": "examplebucket", + "ReplicationConfiguration": { + "Role": "arn:aws:iam::123456789012:role/examplerole", + "Rules": [ + { + "Prefix": "", + "Status": "Enabled", + "Destination": { + "Bucket": "arn:aws:s3:::destinationbucket", + "StorageClass": "STANDARD" + } + } + ] + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?replication", @@ -30538,7 +32770,19 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the request payment configuration for a bucket. By default, the bucket owner pays\n for downloads from the bucket. This configuration parameter enables the bucket owner (only)\n to specify that the person requesting the download will be charged for the download. For\n more information, see Requester Pays\n Buckets.

\n

The following operations are related to PutBucketRequestPayment:

\n ", + "smithy.api#documentation": "

Sets the request payment configuration for a bucket. By default, the bucket owner pays\n for downloads from the bucket. This configuration parameter enables the bucket owner (only)\n to specify that the person requesting the download will be charged for the download. For\n more information, see Requester Pays\n Buckets.

\n

The following operations are related to PutBucketRequestPayment:

\n ", + "smithy.api#examples": [ + { + "title": "Set request payment configuration on a bucket.", + "documentation": "The following example sets request payment configuration on a bucket so that person requesting the download is charged.", + "input": { + "Bucket": "examplebucket", + "RequestPaymentConfiguration": { + "Payer": "Requester" + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?requestPayment", @@ -30563,7 +32807,7 @@ "ContentMD5": { "target": "com.amazonaws.s3#ContentMD5", "traits": { - "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the data. You must use this header as a\n message integrity check to verify that the request body was not corrupted in transit. For\n more information, see RFC\n 1864.

\n

For requests made using the Amazon Web Services Command Line Interface (CLI) or Amazon Web Services SDKs, this field is calculated automatically.

", + "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the data. You must use this header as a message\n integrity check to verify that the request body was not corrupted in transit. For more\n information, see RFC 1864.

\n

For requests made using the Amazon Web Services Command Line Interface (CLI) or Amazon Web Services SDKs, this field is calculated automatically.

", "smithy.api#httpHeader": "Content-MD5" } }, @@ -30608,7 +32852,28 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the tags for a bucket.

\n

Use tags to organize your Amazon Web Services bill to reflect your own cost structure. To do this, sign\n up to get your Amazon Web Services account bill with tag key values included. Then, to see the cost of\n combined resources, organize your billing information according to resources with the same\n tag key values. For example, you can tag several resources with a specific application\n name, and then organize your billing information to see the total cost of that application\n across several services. For more information, see Cost Allocation\n and Tagging and Using Cost Allocation in Amazon S3 Bucket\n Tags.

\n \n

\n When this operation sets the tags for a bucket, it will overwrite any current tags the \n bucket already has. You cannot use this operation to add tags to an existing list of tags.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutBucketTagging action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

\n PutBucketTagging has the following special errors:

\n
    \n
  • \n

    Error code: InvalidTagError\n

    \n \n
  • \n
  • \n

    Error code: MalformedXMLError\n

    \n
      \n
    • \n

      Description: The XML provided does not match the schema.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: OperationAbortedError \n

    \n
      \n
    • \n

      Description: A conflicting conditional action is currently in progress\n against this resource. Please try again.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InternalError\n

    \n
      \n
    • \n

      Description: The service was unable to apply the provided tag to the\n bucket.

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutBucketTagging:

\n ", + "smithy.api#documentation": "

Sets the tags for a bucket.

\n

Use tags to organize your Amazon Web Services bill to reflect your own cost structure. To do this,\n sign up to get your Amazon Web Services account bill with tag key values included. Then, to see the cost\n of combined resources, organize your billing information according to resources with the\n same tag key values. For example, you can tag several resources with a specific application\n name, and then organize your billing information to see the total cost of that application\n across several services. For more information, see Cost Allocation and\n Tagging and Using Cost Allocation in Amazon S3 Bucket\n Tags.

\n \n

When this operation sets the tags for a bucket, it will overwrite any current tags\n the bucket already has. You cannot use this operation to add tags to an existing list of\n tags.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutBucketTagging action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

\n PutBucketTagging has the following special errors:

\n
    \n
  • \n

    Error code: InvalidTagError\n

    \n \n
  • \n
  • \n

    Error code: MalformedXMLError\n

    \n
      \n
    • \n

      Description: The XML provided does not match the schema.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: OperationAbortedError \n

    \n
      \n
    • \n

      Description: A conflicting conditional action is currently in progress\n against this resource. Please try again.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InternalError\n

    \n
      \n
    • \n

      Description: The service was unable to apply the provided tag to the\n bucket.

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutBucketTagging:

\n ", + "smithy.api#examples": [ + { + "title": "Set tags on a bucket", + "documentation": "The following example sets tags on a bucket. Any existing tags are replaced.", + "input": { + "Bucket": "examplebucket", + "Tagging": { + "TagSet": [ + { + "Key": "Key1", + "Value": "Value1" + }, + { + "Key": "Key2", + "Value": "Value2" + } + ] + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?tagging", @@ -30678,7 +32943,20 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the versioning state of an existing bucket.

\n

You can set the versioning state with one of the following values:

\n

\n Enabled—Enables versioning for the objects in the\n bucket. All objects added to the bucket receive a unique version ID.

\n

\n Suspended—Disables versioning for the objects in the\n bucket. All objects added to the bucket receive the version ID null.

\n

If the versioning state has never been set on a bucket, it has no versioning state; a\n GetBucketVersioning request does not return a versioning state value.

\n

In order to enable MFA Delete, you must be the bucket owner. If you are the bucket owner\n and want to enable MFA Delete in the bucket versioning configuration, you must\n include the x-amz-mfa request header and the\n Status and the MfaDelete request elements in a request to set\n the versioning state of the bucket.

\n \n

If you have an object expiration lifecycle policy in your non-versioned bucket and\n you want to maintain the same permanent delete behavior when you enable versioning, you\n must add a noncurrent expiration policy. The noncurrent expiration lifecycle policy will\n manage the deletes of the noncurrent object versions in the version-enabled bucket. (A\n version-enabled bucket maintains one current and zero or more noncurrent object\n versions.) For more information, see Lifecycle and Versioning.

\n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets the versioning state of an existing bucket.

\n

You can set the versioning state with one of the following values:

\n

\n Enabled—Enables versioning for the objects in the\n bucket. All objects added to the bucket receive a unique version ID.

\n

\n Suspended—Disables versioning for the objects in the\n bucket. All objects added to the bucket receive the version ID null.

\n

If the versioning state has never been set on a bucket, it has no versioning state; a\n GetBucketVersioning request does not return a versioning state value.

\n

In order to enable MFA Delete, you must be the bucket owner. If you are the bucket owner\n and want to enable MFA Delete in the bucket versioning configuration, you must include the\n x-amz-mfa request header and the Status and the\n MfaDelete request elements in a request to set the versioning state of the\n bucket.

\n \n

If you have an object expiration lifecycle configuration in your non-versioned bucket and\n you want to maintain the same permanent delete behavior when you enable versioning, you\n must add a noncurrent expiration policy. The noncurrent expiration lifecycle configuration will\n manage the deletes of the noncurrent object versions in the version-enabled bucket. (A\n version-enabled bucket maintains one current and zero or more noncurrent object\n versions.) For more information, see Lifecycle and Versioning.

\n
\n

The following operations are related to PutBucketVersioning:

\n ", + "smithy.api#examples": [ + { + "title": "Set versioning configuration on a bucket", + "documentation": "The following example sets versioning configuration on bucket. The configuration enables versioning on the bucket.", + "input": { + "Bucket": "examplebucket", + "VersioningConfiguration": { + "MFADelete": "Disabled", + "Status": "Enabled" + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?versioning", @@ -30756,6 +33034,24 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Sets the configuration of the website that is specified in the website\n subresource. To configure a bucket as a website, you can add this subresource on the bucket\n with website configuration information such as the file name of the index document and any\n redirect rules. For more information, see Hosting Websites on Amazon S3.

\n

This PUT action requires the S3:PutBucketWebsite permission. By default,\n only the bucket owner can configure the website attached to a bucket; however, bucket\n owners can allow other users to set the website configuration by writing a bucket policy\n that grants them the S3:PutBucketWebsite permission.

\n

To redirect all website requests sent to the bucket's website endpoint, you add a\n website configuration with the following elements. Because all requests are sent to another\n website, you don't need to provide index document name for the bucket.

\n
    \n
  • \n

    \n WebsiteConfiguration\n

    \n
  • \n
  • \n

    \n RedirectAllRequestsTo\n

    \n
  • \n
  • \n

    \n HostName\n

    \n
  • \n
  • \n

    \n Protocol\n

    \n
  • \n
\n

If you want granular control over redirects, you can use the following elements to add\n routing rules that describe conditions for redirecting requests and information about the\n redirect destination. In this case, the website configuration must provide an index\n document for the bucket, because some requests might not be redirected.

\n
    \n
  • \n

    \n WebsiteConfiguration\n

    \n
  • \n
  • \n

    \n IndexDocument\n

    \n
  • \n
  • \n

    \n Suffix\n

    \n
  • \n
  • \n

    \n ErrorDocument\n

    \n
  • \n
  • \n

    \n Key\n

    \n
  • \n
  • \n

    \n RoutingRules\n

    \n
  • \n
  • \n

    \n RoutingRule\n

    \n
  • \n
  • \n

    \n Condition\n

    \n
  • \n
  • \n

    \n HttpErrorCodeReturnedEquals\n

    \n
  • \n
  • \n

    \n KeyPrefixEquals\n

    \n
  • \n
  • \n

    \n Redirect\n

    \n
  • \n
  • \n

    \n Protocol\n

    \n
  • \n
  • \n

    \n HostName\n

    \n
  • \n
  • \n

    \n ReplaceKeyPrefixWith\n

    \n
  • \n
  • \n

    \n ReplaceKeyWith\n

    \n
  • \n
  • \n

    \n HttpRedirectCode\n

    \n
  • \n
\n

Amazon S3 has a limitation of 50 routing rules per website configuration. If you require more\n than 50 routing rules, you can use object redirect. For more information, see Configuring an\n Object Redirect in the Amazon S3 User Guide.

", + "smithy.api#examples": [ + { + "title": "Set website configuration on a bucket", + "documentation": "The following example adds website configuration to a bucket.", + "input": { + "Bucket": "examplebucket", + "ContentMD5": "", + "WebsiteConfiguration": { + "IndexDocument": { + "Suffix": "index.html" + }, + "ErrorDocument": { + "Key": "error.html" + } + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?website", @@ -30824,7 +33120,23 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Adds an object to a bucket. You must have WRITE permissions on a bucket to add an object\n to it.

\n

Amazon S3 never adds partial objects; if you receive a success response, Amazon S3 added the\n entire object to the bucket.

\n

Amazon S3 is a distributed system. If it receives multiple write requests for the same object\n simultaneously, it overwrites all but the last object written. Amazon S3 does not provide object\n locking; if you need this, make sure to build it into your application layer or use\n versioning instead.

\n

To ensure that data is not corrupted traversing the network, use the\n Content-MD5 header. When you use this header, Amazon S3 checks the object\n against the provided MD5 value and, if they do not match, returns an error. Additionally,\n you can calculate the MD5 while putting an object to Amazon S3 and compare the returned ETag to\n the calculated MD5 value.

\n \n
    \n
  • \n

    To successfully complete the PutObject request, you must have the \n s3:PutObject in your IAM permissions.

    \n
  • \n
  • \n

    To successfully change the objects acl of your PutObject request, \n you must have the s3:PutObjectAcl in your IAM permissions.

    \n
  • \n
  • \n

    The Content-MD5 header is required for any request to upload an object\n with a retention period configured using Amazon S3 Object Lock. For more information about\n Amazon S3 Object Lock, see Amazon S3 Object Lock Overview\n in the Amazon S3 User Guide.

    \n
  • \n
\n
\n

\n Server-side Encryption\n

\n

You can optionally request server-side encryption. With server-side encryption, Amazon S3 encrypts \n your data as it writes it to disks in its data centers and decrypts the data\n when you access it. You have the option to provide your own encryption key or use Amazon Web Services\n managed encryption keys (SSE-S3 or SSE-KMS). For more information, see Using Server-Side\n Encryption.

\n

If you request server-side encryption using Amazon Web Services Key Management Service (SSE-KMS), you can enable \n an S3 Bucket Key at the object-level. For more information, see Amazon S3 Bucket Keys in the \n Amazon S3 User Guide.

\n

\n Access Control List (ACL)-Specific Request\n Headers\n

\n

You can use headers to grant ACL- based permissions. By default, all objects are\n private. Only the owner has full access control. When adding a new object, you can grant\n permissions to individual Amazon Web Services accounts or to predefined groups defined by Amazon S3. These\n permissions are then added to the ACL on the object. For more information, see Access Control List\n (ACL) Overview and Managing ACLs Using the REST\n API.

\n

If the bucket that you're uploading objects to uses the bucket owner enforced setting\n for S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that\n use this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control canned\n ACL or an equivalent form of this ACL expressed in the XML format. PUT requests that contain other\n ACLs (for example, custom grants to certain Amazon Web Services accounts) fail and return a\n 400 error with the error code\n AccessControlListNotSupported.

\n

For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, \n all objects written to the bucket by any account will be owned by the bucket owner.

\n
\n

\n Storage Class Options\n

\n

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

\n

\n Versioning\n

\n

If you enable versioning for a bucket, Amazon S3 automatically generates a unique version ID\n for the object being stored. Amazon S3 returns this ID in the response. When you enable\n versioning for a bucket, if Amazon S3 receives multiple write requests for the same object\n simultaneously, it stores all of the objects.

\n

For more information about versioning, see Adding Objects to\n Versioning Enabled Buckets. For information about returning the versioning state\n of a bucket, see GetBucketVersioning.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Adds an object to a bucket. You must have WRITE permissions on a bucket to add an object\n to it.

\n \n

Amazon S3 never adds partial objects; if you receive a success response, Amazon S3 added the\n entire object to the bucket. You cannot use PutObject to only update a\n single piece of metadata for an existing object. You must put the entire object with\n updated metadata if you want to update some values.

\n
\n

Amazon S3 is a distributed system. If it receives multiple write requests for the same object\n simultaneously, it overwrites all but the last object written. To prevent objects from\n being deleted or overwritten, you can use Amazon S3 Object\n Lock.

\n

To ensure that data is not corrupted traversing the network, use the\n Content-MD5 header. When you use this header, Amazon S3 checks the object\n against the provided MD5 value and, if they do not match, returns an error. Additionally,\n you can calculate the MD5 while putting an object to Amazon S3 and compare the returned ETag to\n the calculated MD5 value.

\n \n
    \n
  • \n

    To successfully complete the PutObject request, you must have the\n s3:PutObject in your IAM permissions.

    \n
  • \n
  • \n

    To successfully change the objects acl of your PutObject request,\n you must have the s3:PutObjectAcl in your IAM permissions.

    \n
  • \n
  • \n

    To successfully set the tag-set with your PutObject request, you\n must have the s3:PutObjectTagging in your IAM permissions.

    \n
  • \n
  • \n

    The Content-MD5 header is required for any request to upload an\n object with a retention period configured using Amazon S3 Object Lock. For more\n information about Amazon S3 Object Lock, see Amazon S3 Object Lock\n Overview in the Amazon S3 User Guide.

    \n
  • \n
\n
\n

You have four mutually exclusive options to protect data using server-side encryption in\n Amazon S3, depending on how you choose to manage the encryption keys. Specifically, the\n encryption key options are Amazon S3 managed keys (SSE-S3), Amazon Web Services KMS keys (SSE-KMS or\n DSSE-KMS), and customer-provided keys (SSE-C). Amazon S3 encrypts data with server-side\n encryption by using Amazon S3 managed keys (SSE-S3) by default. You can optionally tell Amazon S3 to\n encrypt data at rest by using server-side encryption with other key options. For more\n information, see Using Server-Side\n Encryption.

\n

When adding a new object, you can use headers to grant ACL-based permissions to\n individual Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are\n then added to the ACL on the object. By default, all objects are private. Only the owner\n has full access control. For more information, see Access Control List (ACL) Overview\n and Managing\n ACLs Using the REST API.

\n

If the bucket that you're uploading objects to uses the bucket owner enforced setting\n for S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that\n use this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control\n canned ACL or an equivalent form of this ACL expressed in the XML format. PUT requests that\n contain other ACLs (for example, custom grants to certain Amazon Web Services accounts) fail and return a\n 400 error with the error code AccessControlListNotSupported.\n For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, all\n objects written to the bucket by any account will be owned by the bucket owner.

\n
\n

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

\n

If you enable versioning for a bucket, Amazon S3 automatically generates a unique version ID\n for the object being stored. Amazon S3 returns this ID in the response. When you enable\n versioning for a bucket, if Amazon S3 receives multiple write requests for the same object\n simultaneously, it stores all of the objects. For more information about versioning, see\n Adding Objects to\n Versioning-Enabled Buckets. For information about returning the versioning state\n of a bucket, see GetBucketVersioning.

\n

For more information about related Amazon S3 APIs, see the following:

\n ", + "smithy.api#examples": [ + { + "title": "To upload an object and specify optional tags", + "documentation": "The following example uploads an object. The request specifies optional object tags. The bucket is versioned, therefore S3 returns version ID of the newly created object.", + "input": { + "Body": "c:\\HappyFace.jpg", + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "Tagging": "key1=value1&key2=value2" + }, + "output": { + "VersionId": "psM2sYY4.o1501dSx8wMvnkOzSBB.V4a", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=PutObject", @@ -30850,7 +33162,21 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Uses the acl subresource to set the access control list (ACL) permissions\n for a new or existing object in an S3 bucket. You must have WRITE_ACP\n permission to set the ACL of an object. For more information, see What\n permissions can I grant? in the Amazon S3 User Guide.

\n

This action is not supported by Amazon S3 on Outposts.

\n

Depending on your application needs, you can choose to set\n the ACL on an object using either the request body or the headers. For example, if you have\n an existing application that updates a bucket ACL using the request body, you can continue\n to use that approach. For more information, see Access Control List (ACL) Overview in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs are disabled and no longer affect permissions. \n You must use policies to grant access to your bucket and the objects in it. Requests to set ACLs or update ACLs fail and \n return the AccessControlListNotSupported error code. Requests to read ACLs are still supported.\n For more information, see Controlling object ownership\n in the Amazon S3 User Guide.

\n
\n

\n Access Permissions\n

\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set\n of grantees and permissions. Specify the canned ACL name as the value of\n x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n x-amz-acl header to set a canned ACL. These parameters map to the set\n of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL)\n Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants list\n objects permission to the two Amazon Web Services accounts identified by their email\n addresses.

    \n

    \n x-amz-grant-read: emailAddress=\"xyz@amazon.com\",\n emailAddress=\"abc@amazon.com\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n

\n Grantee Values\n

\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>lt;/Grantee>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n

\n Versioning\n

\n

The ACL of an object is set at the object version level. By default, PUT sets the ACL of\n the current version of an object. To set the ACL of a different version, use the\n versionId subresource.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Uses the acl subresource to set the access control list (ACL) permissions\n for a new or existing object in an S3 bucket. You must have WRITE_ACP\n permission to set the ACL of an object. For more information, see What\n permissions can I grant? in the Amazon S3 User Guide.

\n

This action is not supported by Amazon S3 on Outposts.

\n

Depending on your application needs, you can choose to set the ACL on an object using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, you can continue to use that approach.\n For more information, see Access Control List (ACL) Overview\n in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs\n are disabled and no longer affect permissions. You must use policies to grant access to\n your bucket and the objects in it. Requests to set ACLs or update ACLs fail and return\n the AccessControlListNotSupported error code. Requests to read ACLs are\n still supported. For more information, see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n
\n
Permissions
\n
\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set\n of grantees and permissions. Specify the canned ACL name as the value of\n x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n x-amz-acl header to set a canned ACL. These parameters map to the set\n of permissions that Amazon S3 supports in an ACL. For more information, see Access Control\n List (ACL) Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants list\n objects permission to the two Amazon Web Services accounts identified by their email\n addresses.

    \n

    \n x-amz-grant-read: emailAddress=\"xyz@amazon.com\",\n emailAddress=\"abc@amazon.com\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>lt;/Grantee>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n
\n
Versioning
\n
\n

The ACL of an object is set at the object version level. By default, PUT sets the ACL of\n the current version of an object. To set the ACL of a different version, use the\n versionId subresource.

\n
\n
\n

The following operations are related to PutObjectAcl:

\n ", + "smithy.api#examples": [ + { + "title": "To grant permissions using object ACL", + "documentation": "The following example adds grants to an object ACL. The first permission grants user1 and user2 FULL_CONTROL and the AllUsers group READ permission.", + "input": { + "AccessControlPolicy": {}, + "Bucket": "examplebucket", + "GrantFullControl": "emailaddress=user1@example.com,emailaddress=user2@example.com", + "GrantRead": "uri=http://acs.amazonaws.com/groups/global/AllUsers", + "Key": "HappyFace.jpg" + }, + "output": {} + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?acl", @@ -30878,7 +33204,7 @@ "ACL": { "target": "com.amazonaws.s3#ObjectCannedACL", "traits": { - "smithy.api#documentation": "

The canned ACL to apply to the object. For more information, see Canned ACL.

", + "smithy.api#documentation": "

The canned ACL to apply to the object. For more information, see Canned\n ACL.

", "smithy.api#httpHeader": "x-amz-acl" } }, @@ -30925,7 +33251,7 @@ "GrantRead": { "target": "com.amazonaws.s3#GrantRead", "traits": { - "smithy.api#documentation": "

Allows grantee to list the objects in the\n bucket.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to list the objects in the bucket.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-read" } }, @@ -30939,21 +33265,21 @@ "GrantWrite": { "target": "com.amazonaws.s3#GrantWrite", "traits": { - "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and overwrites of those objects.

", + "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and\n overwrites of those objects.

", "smithy.api#httpHeader": "x-amz-grant-write" } }, "GrantWriteACP": { "target": "com.amazonaws.s3#GrantWriteACP", "traits": { - "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable\n bucket.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable bucket.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-write-acp" } }, "Key": { "target": "com.amazonaws.s3#ObjectKey", "traits": { - "smithy.api#documentation": "

Key for which the PUT action was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Key for which the PUT action was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {} } @@ -31100,7 +33426,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Places an Object Lock configuration on the specified bucket. The rule specified in the\n Object Lock configuration will be applied by default to every new object placed in the\n specified bucket. For more information, see Locking Objects.\n

\n \n
    \n
  • \n

    The DefaultRetention settings require both a mode and a\n period.

    \n
  • \n
  • \n

    The DefaultRetention period can be either Days\n or Years but you must select one. You cannot specify Days\n and Years at the same time.

    \n
  • \n
  • \n

    You can only enable Object Lock for new buckets. If you want to turn on\n Object Lock for an existing bucket, contact Amazon Web Services Support.

    \n
  • \n
\n
", + "smithy.api#documentation": "

Places an Object Lock configuration on the specified bucket. The rule specified in the\n Object Lock configuration will be applied by default to every new object placed in the\n specified bucket. For more information, see Locking Objects.

\n \n
    \n
  • \n

    The DefaultRetention settings require both a mode and a\n period.

    \n
  • \n
  • \n

    The DefaultRetention period can be either Days or\n Years but you must select one. You cannot specify\n Days and Years at the same time.

    \n
  • \n
  • \n

    You can only enable Object Lock for new buckets. If you want to turn on Object\n Lock for an existing bucket, contact Amazon Web Services Support.

    \n
  • \n
\n
", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?object-lock", @@ -31231,7 +33557,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

If you specified server-side encryption either with an Amazon Web Services KMS key\n or Amazon S3-managed encryption key in your PUT request, the response includes this header. It\n confirms the encryption algorithm that Amazon S3 used to encrypt the object.

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -31259,14 +33585,14 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If x-amz-server-side-encryption is present and has the value of\n aws:kms, this header specifies the ID of the Amazon Web Services Key Management Service\n (Amazon Web Services KMS) symmetric customer managed key that was used for the\n object.

", + "smithy.api#documentation": "

If x-amz-server-side-encryption has a valid value of aws:kms\n or aws:kms:dsse, this header specifies the ID of the Key Management Service (KMS)\n symmetric encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, "SSEKMSEncryptionContext": { "target": "com.amazonaws.s3#SSEKMSEncryptionContext", "traits": { - "smithy.api#documentation": "

If present, specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The\n value of this header is a base64-encoded UTF-8 string holding JSON with the encryption\n context key-value pairs.

", + "smithy.api#documentation": "

If present, specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The\n value of this header is a base64-encoded UTF-8 string holding JSON with the encryption\n context key-value pairs. This value is stored as object metadata and automatically gets passed\n on to Amazon Web Services KMS for future GetObject or CopyObject operations on\n this object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-context" } }, @@ -31274,7 +33600,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the uploaded object uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the uploaded object uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -31295,7 +33621,7 @@ "ACL": { "target": "com.amazonaws.s3#ObjectCannedACL", "traits": { - "smithy.api#documentation": "

The canned ACL to apply to the object. For more information, see Canned\n ACL.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

The canned ACL to apply to the object. For more information, see Canned\n ACL.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-acl" } }, @@ -31310,7 +33636,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name to which the PUT action was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name to which the PUT action was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -31328,14 +33654,14 @@ "ContentDisposition": { "target": "com.amazonaws.s3#ContentDisposition", "traits": { - "smithy.api#documentation": "

Specifies presentational information for the object. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1.

", + "smithy.api#documentation": "

Specifies presentational information for the object. For more information, see https://www.rfc-editor.org/rfc/rfc6266#section-4.

", "smithy.api#httpHeader": "Content-Disposition" } }, "ContentEncoding": { "target": "com.amazonaws.s3#ContentEncoding", "traits": { - "smithy.api#documentation": "

Specifies what content encodings have been applied to the object and thus what decoding\n mechanisms must be applied to obtain the media-type referenced by the Content-Type header\n field. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.

", + "smithy.api#documentation": "

Specifies what content encodings have been applied to the object and thus what decoding\n mechanisms must be applied to obtain the media-type referenced by the Content-Type header\n field. For more information, see https://www.rfc-editor.org/rfc/rfc9110.html#field.content-encoding.

", "smithy.api#httpHeader": "Content-Encoding" } }, @@ -31350,21 +33676,21 @@ "target": "com.amazonaws.s3#ContentLength", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Size of the body in bytes. This parameter is useful when the size of the body cannot be\n determined automatically. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13.

", + "smithy.api#documentation": "

Size of the body in bytes. This parameter is useful when the size of the body cannot be\n determined automatically. For more information, see https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length.

", "smithy.api#httpHeader": "Content-Length" } }, "ContentMD5": { "target": "com.amazonaws.s3#ContentMD5", "traits": { - "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the message (without the headers) according to\n RFC 1864. This header can be used as a message integrity check to verify that the data is\n the same data that was originally sent. Although it is optional, we recommend using the\n Content-MD5 mechanism as an end-to-end integrity check. For more information about REST\n request authentication, see REST\n Authentication.

", + "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the message (without the headers) according to\n RFC 1864. This header can be used as a message integrity check to verify that the data is\n the same data that was originally sent. Although it is optional, we recommend using the\n Content-MD5 mechanism as an end-to-end integrity check. For more information about REST\n request authentication, see REST Authentication.

", "smithy.api#httpHeader": "Content-MD5" } }, "ContentType": { "target": "com.amazonaws.s3#ContentType", "traits": { - "smithy.api#documentation": "

A standard MIME type describing the format of the contents. For more information, see\n http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17.

", + "smithy.api#documentation": "

A standard MIME type describing the format of the contents. For more information, see\n https://www.rfc-editor.org/rfc/rfc9110.html#name-content-type.

", "smithy.api#httpHeader": "Content-Type" } }, @@ -31406,21 +33732,21 @@ "Expires": { "target": "com.amazonaws.s3#Expires", "traits": { - "smithy.api#documentation": "

The date and time at which the object is no longer cacheable. For more information, see\n http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21.

", + "smithy.api#documentation": "

The date and time at which the object is no longer cacheable. For more information, see\n https://www.rfc-editor.org/rfc/rfc7234#section-5.3.

", "smithy.api#httpHeader": "Expires" } }, "GrantFullControl": { "target": "com.amazonaws.s3#GrantFullControl", "traits": { - "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-full-control" } }, "GrantRead": { "target": "com.amazonaws.s3#GrantRead", "traits": { - "smithy.api#documentation": "

Allows grantee to read the object data and its\n metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to read the object data and its metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-read" } }, @@ -31434,7 +33760,7 @@ "GrantWriteACP": { "target": "com.amazonaws.s3#GrantWriteACP", "traits": { - "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-write-acp" } }, @@ -31456,21 +33782,21 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", + "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-storage-class" } }, "WebsiteRedirectLocation": { "target": "com.amazonaws.s3#WebsiteRedirectLocation", "traits": { - "smithy.api#documentation": "

If the bucket is configured as a website, redirects requests for this object to another\n object in the same bucket or to an external URL. Amazon S3 stores the value of this header in\n the object metadata. For information about object metadata, see Object Key and Metadata.

\n

In the following example, the request header sets the redirect to an object\n (anotherPage.html) in the same bucket:

\n

\n x-amz-website-redirect-location: /anotherPage.html\n

\n

In the following example, the request header sets the object redirect to another\n website:

\n

\n x-amz-website-redirect-location: http://www.example.com/\n

\n

For more information about website hosting in Amazon S3, see Hosting Websites on Amazon S3 and How to Configure Website Page\n Redirects.

", + "smithy.api#documentation": "

If the bucket is configured as a website, redirects requests for this object to another\n object in the same bucket or to an external URL. Amazon S3 stores the value of this header in\n the object metadata. For information about object metadata, see Object Key and Metadata.

\n

In the following example, the request header sets the redirect to an object\n (anotherPage.html) in the same bucket:

\n

\n x-amz-website-redirect-location: /anotherPage.html\n

\n

In the following example, the request header sets the object redirect to another\n website:

\n

\n x-amz-website-redirect-location: http://www.example.com/\n

\n

For more information about website hosting in Amazon S3, see Hosting Websites on Amazon S3 and\n How to\n Configure Website Page Redirects.

", "smithy.api#httpHeader": "x-amz-website-redirect-location" } }, @@ -31498,14 +33824,14 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If x-amz-server-side-encryption is present and has the value of\n aws:kms, this header specifies the ID of the Amazon Web Services Key Management Service\n (Amazon Web Services KMS) symmetrical customer managed key that was used for the\n object. If you specify x-amz-server-side-encryption:aws:kms, but do not\n provide x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the Amazon Web Services\n managed key to protect the data. If the KMS key does not exist in the same account\n issuing the command, you must use the full ARN and not just the ID.\n

", + "smithy.api#documentation": "

If x-amz-server-side-encryption has a valid value of aws:kms\n or aws:kms:dsse, this header specifies the ID of the Key Management Service (KMS)\n symmetric encryption customer managed key that was used for the object. If you specify\n x-amz-server-side-encryption:aws:kms or\n x-amz-server-side-encryption:aws:kms:dsse, but do not provide\n x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the Amazon Web Services managed key\n (aws/s3) to protect the data. If the KMS key does not exist in the same\n account that's issuing the command, you must use the full ARN and not just the ID.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, "SSEKMSEncryptionContext": { "target": "com.amazonaws.s3#SSEKMSEncryptionContext", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of this\n header is a base64-encoded UTF-8 string holding JSON with the encryption context key-value\n pairs.

", + "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of\n this header is a base64-encoded UTF-8 string holding JSON with the encryption context\n key-value pairs. This value is stored as object metadata and automatically gets passed on to\n Amazon Web Services KMS for future GetObject or CopyObject operations on this\n object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-context" } }, @@ -31513,7 +33839,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with server-side encryption using AWS KMS (SSE-KMS). Setting this header to true causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with a PUT action doesn’t affect bucket-level settings for S3 Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using Key Management Service (KMS) keys (SSE-KMS). Setting this header to\n true causes Amazon S3 to use an S3 Bucket Key for object encryption with\n SSE-KMS.

\n

Specifying this header with a PUT action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -31547,7 +33873,7 @@ "ObjectLockLegalHoldStatus": { "target": "com.amazonaws.s3#ObjectLockLegalHoldStatus", "traits": { - "smithy.api#documentation": "

Specifies whether a legal hold will be applied to this object. For more information\n about S3 Object Lock, see Object\n Lock.

", + "smithy.api#documentation": "

Specifies whether a legal hold will be applied to this object. For more information\n about S3 Object Lock, see Object Lock.

", "smithy.api#httpHeader": "x-amz-object-lock-legal-hold" } }, @@ -31576,7 +33902,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Places an Object Retention configuration on an object. For more information, see Locking Objects.\n Users or accounts require the s3:PutObjectRetention permission in order to place\n an Object Retention configuration on objects. Bypassing a Governance Retention configuration\n requires the s3:BypassGovernanceRetention permission.\n

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Places an Object Retention configuration on an object. For more information, see Locking Objects.\n Users or accounts require the s3:PutObjectRetention permission in order to\n place an Object Retention configuration on objects. Bypassing a Governance Retention\n configuration requires the s3:BypassGovernanceRetention permission.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?retention", @@ -31688,7 +34014,32 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the supplied tag-set to an object that already exists in a bucket.

\n

A tag is a key-value pair. You can associate tags with an object by sending a PUT\n request against the tagging subresource that is associated with the object. You can\n retrieve tags by sending a GET request. For more information, see GetObjectTagging.

\n

For tagging-related restrictions related to characters and encodings, see Tag\n Restrictions. Note that Amazon S3 limits the maximum number of tags to 10 tags per\n object.

\n

To use this operation, you must have permission to perform the\n s3:PutObjectTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

To put tags of any other version, use the versionId query parameter. You\n also need permission for the s3:PutObjectVersionTagging action.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: InvalidTagError \n

      \n
    • \n
    • \n

      \n Cause: The tag provided was not a valid tag. This error can occur\n if the tag did not pass input validation. For more information, see Object Tagging.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXMLError \n

      \n
    • \n
    • \n

      \n Cause: The XML provided does not match the schema.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAbortedError \n

      \n
    • \n
    • \n

      \n Cause: A conflicting conditional action is currently in\n progress against this resource. Please try again.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError\n

      \n
    • \n
    • \n

      \n Cause: The service was unable to apply the provided tag to the\n object.\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets the supplied tag-set to an object that already exists in a bucket.

\n

A tag is a key-value pair. You can associate tags with an object by sending a PUT\n request against the tagging subresource that is associated with the object. You can\n retrieve tags by sending a GET request. For more information, see GetObjectTagging.

\n

For tagging-related restrictions related to characters and encodings, see Tag\n Restrictions. Note that Amazon S3 limits the maximum number of tags to 10 tags per\n object.

\n

To use this operation, you must have permission to perform the\n s3:PutObjectTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

To put tags of any other version, use the versionId query parameter. You\n also need permission for the s3:PutObjectVersionTagging action.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

\n PutObjectTagging has the following special errors:

\n
    \n
  • \n
      \n
    • \n

      \n Code: InvalidTagError \n

      \n
    • \n
    • \n

      \n Cause: The tag provided was not a valid tag. This error can occur\n if the tag did not pass input validation. For more information, see Object\n Tagging.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXMLError \n

      \n
    • \n
    • \n

      \n Cause: The XML provided does not match the schema.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAbortedError \n

      \n
    • \n
    • \n

      \n Cause: A conflicting conditional action is currently in progress\n against this resource. Please try again.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError\n

      \n
    • \n
    • \n

      \n Cause: The service was unable to apply the provided tag to the\n object.\n

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutObjectTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To add tags to an existing object", + "documentation": "The following example adds tags to an existing object.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "Tagging": { + "TagSet": [ + { + "Key": "Key3", + "Value": "Value3" + }, + { + "Key": "Key4", + "Value": "Value4" + } + ] + } + }, + "output": { + "VersionId": "null" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?tagging", @@ -31717,7 +34068,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -31794,7 +34145,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates or modifies the PublicAccessBlock configuration for an Amazon S3 bucket.\n To use this operation, you must have the s3:PutBucketPublicAccessBlock\n permission. For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n \n

When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or\n an object, it checks the PublicAccessBlock configuration for both the\n bucket (or the bucket that contains the object) and the bucket owner's account. If the\n PublicAccessBlock configurations are different between the bucket and\n the account, Amazon S3 uses the most restrictive combination of the bucket-level and\n account-level settings.

\n
\n

For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Creates or modifies the PublicAccessBlock configuration for an Amazon S3 bucket.\n To use this operation, you must have the s3:PutBucketPublicAccessBlock\n permission. For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n \n

When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or\n an object, it checks the PublicAccessBlock configuration for both the\n bucket (or the bucket that contains the object) and the bucket owner's account. If the\n PublicAccessBlock configurations are different between the bucket and\n the account, Amazon S3 uses the most restrictive combination of the bucket-level and\n account-level settings.

\n
\n

For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

\n

The following operations are related to PutPublicAccessBlock:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?publicAccessBlock", @@ -32047,7 +34398,7 @@ "Role": { "target": "com.amazonaws.s3#Role", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Identity and Access Management (IAM) role that\n Amazon S3 assumes when replicating objects. For more information, see How to Set Up\n Replication in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Identity and Access Management (IAM) role that Amazon S3 assumes when\n replicating objects. For more information, see How to Set Up Replication\n in the Amazon S3 User Guide.

", "smithy.api#required": {} } }, @@ -32101,13 +34452,13 @@ "SourceSelectionCriteria": { "target": "com.amazonaws.s3#SourceSelectionCriteria", "traits": { - "smithy.api#documentation": "

A container that describes additional filters for identifying the source objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects. Currently, Amazon S3 supports only the filter that you can specify for objects created\n with server-side encryption using a customer managed key stored in Amazon Web Services Key Management\n Service (SSE-KMS).

" + "smithy.api#documentation": "

A container that describes additional filters for identifying the source objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects. Currently, Amazon S3 supports only the filter that you can specify for objects created\n with server-side encryption using a customer managed key stored in Amazon Web Services Key Management Service\n (SSE-KMS).

" } }, "ExistingObjectReplication": { "target": "com.amazonaws.s3#ExistingObjectReplication", "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

Optional configuration to replicate existing source bucket objects. For more\n information, see Replicating Existing Objects in the Amazon S3 User Guide.\n

" } }, "Destination": { @@ -32384,7 +34735,24 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Restores an archived copy of an object back into Amazon S3

\n

This action is not supported by Amazon S3 on Outposts.

\n

This action performs the following types of requests:

\n
    \n
  • \n

    \n select - Perform a select query on an archived object

    \n
  • \n
  • \n

    \n restore an archive - Restore an archived object

    \n
  • \n
\n

To use this operation, you must have permissions to perform the\n s3:RestoreObject action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

\n Querying Archives with Select Requests\n

\n

You use a select type of request to perform SQL queries on archived objects. The\n archived objects that are being queried by the select request must be formatted as\n uncompressed comma-separated values (CSV) files. You can run queries and custom analytics\n on your archived data without having to restore your data to a hotter Amazon S3 tier. For an\n overview about select requests, see Querying Archived Objects in the Amazon S3 User Guide.

\n

When making a select request, do the following:

\n
    \n
  • \n

    Define an output location for the select query's output. This must be an Amazon S3\n bucket in the same Amazon Web Services Region as the bucket that contains the archive object that is\n being queried. The Amazon Web Services account that initiates the job must have permissions to write\n to the S3 bucket. You can specify the storage class and encryption for the output\n objects stored in the bucket. For more information about output, see Querying Archived Objects\n in the Amazon S3 User Guide.

    \n

    For more information about the S3 structure in the request body, see\n the following:

    \n \n
  • \n
  • \n

    Define the SQL expression for the SELECT type of restoration for your\n query in the request body's SelectParameters structure. You can use\n expressions like the following examples.

    \n
      \n
    • \n

      The following expression returns all records from the specified\n object.

      \n

      \n SELECT * FROM Object\n

      \n
    • \n
    • \n

      Assuming that you are not using any headers for data stored in the object,\n you can specify columns with positional headers.

      \n

      \n SELECT s._1, s._2 FROM Object s WHERE s._3 > 100\n

      \n
    • \n
    • \n

      If you have headers and you set the fileHeaderInfo in the\n CSV structure in the request body to USE, you can\n specify headers in the query. (If you set the fileHeaderInfo field\n to IGNORE, the first row is skipped for the query.) You cannot mix\n ordinal positions with header column names.

      \n

      \n SELECT s.Id, s.FirstName, s.SSN FROM S3Object s\n

      \n
    • \n
    \n
  • \n
\n

For more information about using SQL with S3 Glacier Select restore, see SQL Reference for Amazon S3 Select and\n S3 Glacier Select in the Amazon S3 User Guide.

\n

When making a select request, you can also do the following:

\n
    \n
  • \n

    To expedite your queries, specify the Expedited tier. For more\n information about tiers, see \"Restoring Archives,\" later in this topic.

    \n
  • \n
  • \n

    Specify details about the data serialization format of both the input object that\n is being queried and the serialization of the CSV-encoded query results.

    \n
  • \n
\n

The following are additional important facts about the select feature:

\n
    \n
  • \n

    The output results are new Amazon S3 objects. Unlike archive retrievals, they are\n stored until explicitly deleted-manually or through a lifecycle policy.

    \n
  • \n
  • \n

    You can issue more than one select request on the same Amazon S3 object. Amazon S3 doesn't\n deduplicate requests, so avoid issuing duplicate requests.

    \n
  • \n
  • \n

    Amazon S3 accepts a select request even if the object has already been restored. A\n select request doesn’t return error response 409.

    \n
  • \n
\n

\n Restoring objects\n

\n

Objects that you archive to the S3 Glacier or\n S3 Glacier Deep Archive storage class, and S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers are not accessible in real time. For objects in\n Archive Access or Deep Archive Access tiers you must first initiate a restore request, and\n then wait until the object is moved into the Frequent Access tier. For objects in\n S3 Glacier or S3 Glacier Deep Archive storage classes you must\n first initiate a restore request, and then wait until a temporary copy of the object is\n available. To access an archived object, you must restore the object for the duration\n (number of days) that you specify.

\n

To restore a specific object version, you can provide a version ID. If you don't provide\n a version ID, Amazon S3 restores the current version.

\n

When restoring an archived object (or using a select request), you can specify one of\n the following data access tier options in the Tier element of the request\n body:

\n
    \n
  • \n

    \n Expedited - Expedited retrievals allow you to quickly access your\n data stored in the S3 Glacier storage class or S3 Intelligent-Tiering Archive\n tier when occasional urgent requests for a subset of archives are required. For all\n but the largest archived objects (250 MB+), data accessed using Expedited retrievals\n is typically made available within 1–5 minutes. Provisioned capacity ensures that\n retrieval capacity for Expedited retrievals is available when you need it. Expedited\n retrievals and provisioned capacity are not available for objects stored in the\n S3 Glacier Deep Archive storage class or S3 Intelligent-Tiering Deep Archive tier.

    \n
  • \n
  • \n

    \n Standard - Standard retrievals allow you to access any of your\n archived objects within several hours. This is the default option for retrieval\n requests that do not specify the retrieval option. Standard retrievals typically\n finish within 3–5 hours for objects stored in the S3 Glacier storage\n class or S3 Intelligent-Tiering Archive tier. They typically finish within 12 hours for\n objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier. Standard retrievals are free for objects stored in\n S3 Intelligent-Tiering.

    \n
  • \n
  • \n

    \n Bulk - Bulk retrievals are the lowest-cost retrieval option in\n S3 Glacier, enabling you to retrieve large amounts, even petabytes, of data\n inexpensively. Bulk retrievals typically finish within 5–12 hours for objects stored\n in the S3 Glacier storage class or S3 Intelligent-Tiering Archive tier. They\n typically finish within 48 hours for objects stored in the\n S3 Glacier Deep Archive storage class or S3 Intelligent-Tiering Deep Archive tier. Bulk\n retrievals are free for objects stored in S3 Intelligent-Tiering.

    \n
  • \n
\n

For more information about archive retrieval options and provisioned capacity for\n Expedited data access, see Restoring Archived Objects in the Amazon S3 User Guide.

\n

You can use Amazon S3 restore speed upgrade to change the restore speed to a faster speed\n while it is in progress. For more information, see \n Upgrading the speed of an in-progress restore in the\n Amazon S3 User Guide.

\n

To get the status of object restoration, you can send a HEAD request.\n Operations return the x-amz-restore header, which provides information about\n the restoration status, in the response. You can use Amazon S3 event notifications to notify you\n when a restore is initiated or completed. For more information, see Configuring Amazon S3 Event Notifications in\n the Amazon S3 User Guide.

\n

After restoring an archived object, you can update the restoration period by reissuing\n the request with a new period. Amazon S3 updates the restoration period relative to the current\n time and charges only for the request-there are no data transfer charges. You cannot\n update the restoration period when Amazon S3 is actively processing your current restore request\n for the object.

\n

If your bucket has a lifecycle configuration with a rule that includes an expiration\n action, the object expiration overrides the life span that you specify in a restore\n request. For example, if you restore an object copy for 10 days, but the object is\n scheduled to expire in 3 days, Amazon S3 deletes the object in 3 days. For more information\n about lifecycle configuration, see PutBucketLifecycleConfiguration and Object Lifecycle Management in\n Amazon S3 User Guide.

\n

\n Responses\n

\n

A successful action returns either the 200 OK or 202\n Accepted status code.

\n
    \n
  • \n

    If the object is not previously restored, then Amazon S3 returns 202\n Accepted in the response.

    \n
  • \n
  • \n

    If the object is previously restored, Amazon S3 returns 200 OK in the\n response.

    \n
  • \n
\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress\n

      \n
    • \n
    • \n

      \n Cause: Object restore is already in progress. (This error does not\n apply to SELECT type requests.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: GlacierExpeditedRetrievalNotAvailable\n

      \n
    • \n
    • \n

      \n Cause: expedited retrievals are currently not available. Try again\n later. (Returned if there is insufficient capacity to process the Expedited\n request. This error applies only to Expedited retrievals and not to\n S3 Standard or Bulk retrievals.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 503\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: N/A\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Restores an archived copy of an object back into Amazon S3

\n

This action is not supported by Amazon S3 on Outposts.

\n

This action performs the following types of requests:

\n
    \n
  • \n

    \n select - Perform a select query on an archived object

    \n
  • \n
  • \n

    \n restore an archive - Restore an archived object

    \n
  • \n
\n

For more information about the S3 structure in the request body, see the\n following:

\n \n

Define the SQL expression for the SELECT type of restoration for your\n query in the request body's SelectParameters structure. You can use\n expressions like the following examples.

\n
    \n
  • \n

    The following expression returns all records from the specified\n object.

    \n

    \n SELECT * FROM Object\n

    \n
  • \n
  • \n

    Assuming that you are not using any headers for data stored in the object,\n you can specify columns with positional headers.

    \n

    \n SELECT s._1, s._2 FROM Object s WHERE s._3 > 100\n

    \n
  • \n
  • \n

    If you have headers and you set the fileHeaderInfo in the\n CSV structure in the request body to USE, you can\n specify headers in the query. (If you set the fileHeaderInfo field\n to IGNORE, the first row is skipped for the query.) You cannot mix\n ordinal positions with header column names.

    \n

    \n SELECT s.Id, s.FirstName, s.SSN FROM S3Object s\n

    \n
  • \n
\n

When making a select request, you can also do the following:

\n
    \n
  • \n

    To expedite your queries, specify the Expedited tier. For more\n information about tiers, see \"Restoring Archives,\" later in this topic.

    \n
  • \n
  • \n

    Specify details about the data serialization format of both the input object that\n is being queried and the serialization of the CSV-encoded query results.

    \n
  • \n
\n

The following are additional important facts about the select feature:

\n
    \n
  • \n

    The output results are new Amazon S3 objects. Unlike archive retrievals, they are\n stored until explicitly deleted-manually or through a lifecycle configuration.

    \n
  • \n
  • \n

    You can issue more than one select request on the same Amazon S3 object. Amazon S3 doesn't\n duplicate requests, so avoid issuing duplicate requests.

    \n
  • \n
  • \n

    Amazon S3 accepts a select request even if the object has already been restored. A\n select request doesn’t return error response 409.

    \n
  • \n
\n
\n
Permissions
\n
\n

To use this operation, you must have permissions to perform the\n s3:RestoreObject action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n
\n
Restoring objects
\n
\n

Objects that you archive to the S3 Glacier Flexible Retrieval Flexible Retrieval or\n S3 Glacier Deep Archive storage class, and S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, are not accessible in real time. For objects in the\n S3 Glacier Flexible Retrieval Flexible Retrieval or S3 Glacier Deep Archive storage\n classes, you must first initiate a restore request, and then wait until a temporary copy of\n the object is available. If you want a permanent copy of the object, create a copy of it in\n the Amazon S3 Standard storage class in your S3 bucket. To access an archived object, you must\n restore the object for the duration (number of days) that you specify. For objects in the\n Archive Access or Deep Archive Access tiers of S3 Intelligent-Tiering, you must first\n initiate a restore request, and then wait until the object is moved into the Frequent\n Access tier.

\n

To restore a specific object version, you can provide a version ID. If you don't provide\n a version ID, Amazon S3 restores the current version.

\n

When restoring an archived object, you can specify one of the following data access tier\n options in the Tier element of the request body:

\n
    \n
  • \n

    \n Expedited - Expedited retrievals allow you to quickly access your\n data stored in the S3 Glacier Flexible Retrieval Flexible Retrieval storage class or\n S3 Intelligent-Tiering Archive tier when occasional urgent requests for restoring archives\n are required. For all but the largest archived objects (250 MB+), data accessed using\n Expedited retrievals is typically made available within 1–5 minutes. Provisioned\n capacity ensures that retrieval capacity for Expedited retrievals is available when\n you need it. Expedited retrievals and provisioned capacity are not available for\n objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier.

    \n
  • \n
  • \n

    \n Standard - Standard retrievals allow you to access any of your\n archived objects within several hours. This is the default option for retrieval\n requests that do not specify the retrieval option. Standard retrievals typically\n finish within 3–5 hours for objects stored in the S3 Glacier Flexible Retrieval Flexible\n Retrieval storage class or S3 Intelligent-Tiering Archive tier. They typically finish within\n 12 hours for objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier. Standard retrievals are free for objects stored in\n S3 Intelligent-Tiering.

    \n
  • \n
  • \n

    \n Bulk - Bulk retrievals free for objects stored in the S3 Glacier\n Flexible Retrieval and S3 Intelligent-Tiering storage classes, enabling you to\n retrieve large amounts, even petabytes, of data at no cost. Bulk retrievals typically\n finish within 5–12 hours for objects stored in the S3 Glacier Flexible Retrieval\n Flexible Retrieval storage class or S3 Intelligent-Tiering Archive tier. Bulk retrievals are\n also the lowest-cost retrieval option when restoring objects from\n S3 Glacier Deep Archive. They typically finish within 48 hours for objects\n stored in the S3 Glacier Deep Archive storage class or S3 Intelligent-Tiering Deep Archive\n tier.

    \n
  • \n
\n

For more information about archive retrieval options and provisioned capacity for\n Expedited data access, see Restoring Archived Objects in\n the Amazon S3 User Guide.

\n

You can use Amazon S3 restore speed upgrade to change the restore speed to a faster speed\n while it is in progress. For more information, see Upgrading the speed of an in-progress restore in the\n Amazon S3 User Guide.

\n

To get the status of object restoration, you can send a HEAD request.\n Operations return the x-amz-restore header, which provides information about\n the restoration status, in the response. You can use Amazon S3 event notifications to notify you\n when a restore is initiated or completed. For more information, see Configuring Amazon S3\n Event Notifications in the Amazon S3 User Guide.

\n

After restoring an archived object, you can update the restoration period by reissuing\n the request with a new period. Amazon S3 updates the restoration period relative to the current\n time and charges only for the request-there are no data transfer charges. You cannot\n update the restoration period when Amazon S3 is actively processing your current restore request\n for the object.

\n

If your bucket has a lifecycle configuration with a rule that includes an expiration\n action, the object expiration overrides the life span that you specify in a restore\n request. For example, if you restore an object copy for 10 days, but the object is\n scheduled to expire in 3 days, Amazon S3 deletes the object in 3 days. For more information\n about lifecycle configuration, see PutBucketLifecycleConfiguration and Object Lifecycle Management\n in Amazon S3 User Guide.

\n
\n
Responses
\n
\n

A successful action returns either the 200 OK or 202 Accepted\n status code.

\n
    \n
  • \n

    If the object is not previously restored, then Amazon S3 returns 202\n Accepted in the response.

    \n
  • \n
  • \n

    If the object is previously restored, Amazon S3 returns 200 OK in the\n response.

    \n
  • \n
\n
    \n
  • \n

    Special errors:

    \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress\n

      \n
    • \n
    • \n

      \n Cause: Object restore is already in progress. (This error does not\n apply to SELECT type requests.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: GlacierExpeditedRetrievalNotAvailable\n

      \n
    • \n
    • \n

      \n Cause: expedited retrievals are currently not available. Try again\n later. (Returned if there is insufficient capacity to process the Expedited\n request. This error applies only to Expedited retrievals and not to\n S3 Standard or Bulk retrievals.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 503\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: N/A\n

      \n
    • \n
    \n
  • \n
\n
\n
\n

The following operations are related to RestoreObject:

\n ", + "smithy.api#examples": [ + { + "title": "To restore an archived object", + "documentation": "The following example restores for one day an archived copy of an object back into Amazon S3 bucket.", + "input": { + "Bucket": "examplebucket", + "Key": "archivedobjectkey", + "RestoreRequest": { + "Days": 1, + "GlacierJobParameters": { + "Tier": "Expedited" + } + } + }, + "output": {} + } + ], "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?restore&x-id=RestoreObject", @@ -32419,7 +34787,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the object to restore.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the object to restore.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -32660,7 +35028,7 @@ "KeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric customer managed key\n to use for encrypting inventory reports.

", + "smithy.api#documentation": "

Specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key to use for\n encrypting inventory reports.

", "smithy.api#required": {} } } @@ -32721,7 +35089,7 @@ "target": "com.amazonaws.s3#SelectObjectContentOutput" }, "traits": { - "smithy.api#documentation": "

This action filters the contents of an Amazon S3 object based on a simple structured query\n language (SQL) statement. In the request, along with the SQL expression, you must also\n specify a data serialization format (JSON, CSV, or Apache Parquet) of the object. Amazon S3 uses\n this format to parse object data into records, and returns only records that match the\n specified SQL expression. You must also specify the data serialization format for the\n response.

\n

This action is not supported by Amazon S3 on Outposts.

\n

For more information about Amazon S3 Select,\n see Selecting Content from\n Objects and SELECT\n Command in the Amazon S3 User Guide.

\n

For more information about using SQL with Amazon S3 Select, see SQL Reference for Amazon S3 Select\n and S3 Glacier Select in the Amazon S3 User Guide.

\n

\n

\n Permissions\n

\n

You must have s3:GetObject permission for this operation. Amazon S3 Select does\n not support anonymous access. For more information about permissions, see Specifying Permissions in a Policy\n in the Amazon S3 User Guide.

\n

\n

\n Object Data Formats\n

\n

You can use Amazon S3 Select to query objects that have the following format\n properties:

\n
    \n
  • \n

    \n CSV, JSON, and Parquet - Objects must be in CSV, JSON, or\n Parquet format.

    \n
  • \n
  • \n

    \n UTF-8 - UTF-8 is the only encoding type Amazon S3 Select\n supports.

    \n
  • \n
  • \n

    \n GZIP or BZIP2 - CSV and JSON files can be compressed using\n GZIP or BZIP2. GZIP and BZIP2 are the only compression formats that Amazon S3 Select\n supports for CSV and JSON files. Amazon S3 Select supports columnar compression for\n Parquet using GZIP or Snappy. Amazon S3 Select does not support whole-object compression\n for Parquet objects.

    \n
  • \n
  • \n

    \n Server-side encryption - Amazon S3 Select supports querying\n objects that are protected with server-side encryption.

    \n

    For objects that are encrypted with customer-provided encryption keys (SSE-C), you\n must use HTTPS, and you must use the headers that are documented in the GetObject. For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

    \n

    For objects that are encrypted with Amazon S3 managed encryption keys (SSE-S3) and\n Amazon Web Services KMS keys (SSE-KMS),\n server-side encryption is handled transparently, so you don't need to specify\n anything. For more information about server-side encryption, including SSE-S3 and\n SSE-KMS, see Protecting Data Using\n Server-Side Encryption in the Amazon S3 User Guide.

    \n
  • \n
\n

\n Working with the Response Body\n

\n

Given the response size is unknown, Amazon S3 Select streams the response as a series of\n messages and includes a Transfer-Encoding header with chunked as\n its value in the response. For more information, see Appendix: SelectObjectContent\n Response.

\n

\n

\n GetObject Support\n

\n

The SelectObjectContent action does not support the following\n GetObject functionality. For more information, see GetObject.

\n
    \n
  • \n

    \n Range: Although you can specify a scan range for an Amazon S3 Select request\n (see SelectObjectContentRequest - ScanRange in the request parameters),\n you cannot specify the range of bytes of an object to return.

    \n
  • \n
  • \n

    GLACIER, DEEP_ARCHIVE and REDUCED_REDUNDANCY storage classes: You cannot specify\n the GLACIER, DEEP_ARCHIVE, or REDUCED_REDUNDANCY storage classes. For\n more information, about storage classes see Storage Classes\n in the Amazon S3 User Guide.

    \n
  • \n
\n

\n

\n Special Errors\n

\n

For a list of special errors for this operation, see List of\n SELECT Object Content Error Codes\n

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This action filters the contents of an Amazon S3 object based on a simple structured query\n language (SQL) statement. In the request, along with the SQL expression, you must also\n specify a data serialization format (JSON, CSV, or Apache Parquet) of the object. Amazon S3 uses\n this format to parse object data into records, and returns only records that match the\n specified SQL expression. You must also specify the data serialization format for the\n response.

\n

This action is not supported by Amazon S3 on Outposts.

\n

For more information about Amazon S3 Select, see Selecting Content from\n Objects and SELECT\n Command in the Amazon S3 User Guide.

\n

\n
\n
Permissions
\n
\n

You must have s3:GetObject permission for this operation. Amazon S3 Select does\n not support anonymous access. For more information about permissions, see Specifying\n Permissions in a Policy in the Amazon S3 User Guide.

\n
\n
Object Data Formats
\n
\n

You can use Amazon S3 Select to query objects that have the following format\n properties:

\n
    \n
  • \n

    \n CSV, JSON, and Parquet - Objects must be in CSV, JSON, or\n Parquet format.

    \n
  • \n
  • \n

    \n UTF-8 - UTF-8 is the only encoding type Amazon S3 Select\n supports.

    \n
  • \n
  • \n

    \n GZIP or BZIP2 - CSV and JSON files can be compressed using\n GZIP or BZIP2. GZIP and BZIP2 are the only compression formats that Amazon S3 Select\n supports for CSV and JSON files. Amazon S3 Select supports columnar compression for\n Parquet using GZIP or Snappy. Amazon S3 Select does not support whole-object compression\n for Parquet objects.

    \n
  • \n
  • \n

    \n Server-side encryption - Amazon S3 Select supports querying\n objects that are protected with server-side encryption.

    \n

    For objects that are encrypted with customer-provided encryption keys (SSE-C), you\n must use HTTPS, and you must use the headers that are documented in the GetObject. For more information about SSE-C, see Server-Side\n Encryption (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

    \n

    For objects that are encrypted with Amazon S3 managed keys (SSE-S3) and Amazon Web Services KMS keys\n (SSE-KMS), server-side encryption is handled transparently, so you don't need to\n specify anything. For more information about server-side encryption, including SSE-S3\n and SSE-KMS, see Protecting Data Using\n Server-Side Encryption in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
Working with the Response Body
\n
\n

Given the response size is unknown, Amazon S3 Select streams the response as a series of\n messages and includes a Transfer-Encoding header with chunked as\n its value in the response. For more information, see Appendix: SelectObjectContent\n Response.

\n
\n
GetObject Support
\n
\n

The SelectObjectContent action does not support the following\n GetObject functionality. For more information, see GetObject.

\n
    \n
  • \n

    \n Range: Although you can specify a scan range for an Amazon S3 Select request\n (see SelectObjectContentRequest - ScanRange in the request parameters),\n you cannot specify the range of bytes of an object to return.

    \n
  • \n
  • \n

    The GLACIER, DEEP_ARCHIVE, and REDUCED_REDUNDANCY storage classes, or the ARCHIVE_ACCESS and \n DEEP_ARCHIVE_ACCESS access tiers of \n the INTELLIGENT_TIERING storage class: You cannot query objects in \n the GLACIER, DEEP_ARCHIVE, or REDUCED_REDUNDANCY storage classes, nor objects in the \n ARCHIVE_ACCESS or \n DEEP_ARCHIVE_ACCESS access tiers of \n the INTELLIGENT_TIERING storage class. For\n more information about storage classes, see Using Amazon S3 storage\n classes in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
Special Errors
\n
\n

For a list of special errors for this operation, see List of\n SELECT Object Content Error Codes\n

\n
\n
\n

The following operations are related to SelectObjectContent:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?select&select-type=2&x-id=SelectObjectContent", @@ -32929,6 +35297,12 @@ "traits": { "smithy.api#enumValue": "aws:kms" } + }, + "aws_kms_dsse": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "aws:kms:dsse" + } } } }, @@ -32945,12 +35319,12 @@ "KMSMasterKeyID": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Amazon Web Services Key Management Service (KMS) customer Amazon Web Services KMS key ID to use for the default\n encryption. This parameter is allowed if and only if SSEAlgorithm is set to\n aws:kms.

\n

You can specify the key ID or the Amazon Resource Name (ARN) of the KMS key. However, if\n you are using encryption with cross-account or Amazon Web Services service operations you must use a fully qualified KMS\n key ARN. For more information, see Using encryption for cross-account operations.

\n

\n For example:\n

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN:\n arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n \n

Amazon S3 only supports symmetric KMS keys and not asymmetric KMS keys. For more information, see\n Using symmetric and\n asymmetric keys in the Amazon Web Services Key Management Service Developer Guide.

\n
" + "smithy.api#documentation": "

Amazon Web Services Key Management Service (KMS) customer Amazon Web Services KMS key ID to use for the default\n encryption. This parameter is allowed if and only if SSEAlgorithm is set to\n aws:kms.

\n

You can specify the key ID or the Amazon Resource Name (ARN) of the KMS key. If you use\n a key ID, you can run into a LogDestination undeliverable error when creating a VPC flow\n log.

\n

If you are using encryption with cross-account or Amazon Web Services service operations you must use\n a fully qualified KMS key ARN. For more information, see Using encryption for cross-account operations.

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN:\n arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n \n

Amazon S3 only supports symmetric encryption KMS keys. For more information, see Asymmetric keys in Amazon Web Services KMS in the Amazon Web Services Key Management Service\n Developer Guide.

\n
" } } }, "traits": { - "smithy.api#documentation": "

Describes the default server-side encryption to apply to new objects in the bucket. If a\n PUT Object request doesn't specify any server-side encryption, this default encryption will\n be applied. If you don't specify a customer managed key at configuration, Amazon S3 automatically creates \n an Amazon Web Services KMS key in your Amazon Web Services account the first time that you add an object encrypted with\n SSE-KMS to a bucket. By default, Amazon S3 uses this KMS key for SSE-KMS. For more information, see PUT Bucket encryption in\n the Amazon S3 API Reference.

" + "smithy.api#documentation": "

Describes the default server-side encryption to apply to new objects in the bucket. If a\n PUT Object request doesn't specify any server-side encryption, this default encryption will\n be applied. If you don't specify a customer managed key at configuration, Amazon S3 automatically creates\n an Amazon Web Services KMS key in your Amazon Web Services account the first time that you add an object encrypted\n with SSE-KMS to a bucket. By default, Amazon S3 uses this KMS key for SSE-KMS. For more\n information, see PUT Bucket encryption in\n the Amazon S3 API Reference.

" } }, "com.amazonaws.s3#ServerSideEncryptionConfiguration": { @@ -32983,7 +35357,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key with server-side encryption using KMS (SSE-KMS) for new objects in the bucket. Existing objects are not affected. Setting the BucketKeyEnabled element to true causes Amazon S3 to use an S3 Bucket Key. By default, S3 Bucket Key is not enabled.

\n

For more information, see Amazon S3 Bucket Keys in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key with server-side encryption using KMS\n (SSE-KMS) for new objects in the bucket. Existing objects are not affected. Setting the\n BucketKeyEnabled element to true causes Amazon S3 to use an S3\n Bucket Key. By default, S3 Bucket Key is not enabled.

\n

For more information, see Amazon S3 Bucket Keys in the\n Amazon S3 User Guide.

" } } }, @@ -33021,7 +35395,7 @@ "SseKmsEncryptedObjects": { "target": "com.amazonaws.s3#SseKmsEncryptedObjects", "traits": { - "smithy.api#documentation": "

A container for filter information for the selection of Amazon S3 objects encrypted with Amazon Web Services\n KMS. If you include SourceSelectionCriteria in the replication configuration,\n this element is required.

" + "smithy.api#documentation": "

A container for filter information for the selection of Amazon S3 objects encrypted with\n Amazon Web Services KMS. If you include SourceSelectionCriteria in the replication\n configuration, this element is required.

" } }, "ReplicaModifications": { @@ -33032,7 +35406,7 @@ } }, "traits": { - "smithy.api#documentation": "

A container that describes additional filters for identifying the source objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects. Currently, Amazon S3 supports only the filter that you can specify for objects created\n with server-side encryption using a customer managed key stored in Amazon Web Services Key Management\n Service (SSE-KMS).

" + "smithy.api#documentation": "

A container that describes additional filters for identifying the source objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects. Currently, Amazon S3 supports only the filter that you can specify for objects created\n with server-side encryption using a customer managed key stored in Amazon Web Services Key Management Service\n (SSE-KMS).

" } }, "com.amazonaws.s3#SseKmsEncryptedObjects": { @@ -33176,6 +35550,12 @@ "traits": { "smithy.api#enumValue": "GLACIER_IR" } + }, + "SNOW": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SNOW" + } } } }, @@ -33331,7 +35711,7 @@ } }, "traits": { - "smithy.api#documentation": "

Container for granting information.

\n

Buckets that use the bucket owner enforced setting for Object\n Ownership don't support target grants. For more information, see Permissions server access log delivery in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Container for granting information.

\n

Buckets that use the bucket owner enforced setting for Object Ownership don't support\n target grants. For more information, see Permissions server access log delivery in the\n Amazon S3 User Guide.

" } }, "com.amazonaws.s3#TargetGrants": { @@ -33383,7 +35763,7 @@ "AccessTier": { "target": "com.amazonaws.s3#IntelligentTieringAccessTier", "traits": { - "smithy.api#documentation": "

S3 Intelligent-Tiering access tier. See Storage class for\n automatically optimizing frequently and infrequently accessed objects for a list\n of access tiers in the S3 Intelligent-Tiering storage class.

", + "smithy.api#documentation": "

S3 Intelligent-Tiering access tier. See Storage class\n for automatically optimizing frequently and infrequently accessed objects for a\n list of access tiers in the S3 Intelligent-Tiering storage class.

", "smithy.api#required": {} } } @@ -33556,7 +35936,7 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Uploads a part in a multipart upload.

\n \n

In this operation, you provide part data in your request. However, you have an option\n to specify your existing Amazon S3 object as a data source for the part you are uploading. To\n upload a part from an existing object, you use the UploadPartCopy operation.\n

\n
\n

You must initiate a multipart upload (see CreateMultipartUpload)\n before you can upload any part. In response to your initiate request, Amazon S3 returns an\n upload ID, a unique identifier, that you must include in your upload part request.

\n

Part numbers can be any number from 1 to 10,000, inclusive. A part number uniquely\n identifies a part and also defines its position within the object being created. If you\n upload a new part using the same part number that was used with a previous part, the\n previously uploaded part is overwritten.

\n

For information about maximum and minimum part sizes and other multipart upload specifications, see Multipart upload limits in the Amazon S3 User Guide.

\n

To ensure that data is not corrupted when traversing the network, specify the\n Content-MD5 header in the upload part request. Amazon S3 checks the part data\n against the provided MD5 value. If they do not match, Amazon S3 returns an error.

\n

If the upload request is signed with Signature Version 4, then Amazon Web Services S3 uses the\n x-amz-content-sha256 header as a checksum instead of\n Content-MD5. For more information see Authenticating Requests: Using the Authorization Header (Amazon Web Services Signature Version\n 4).

\n

\n Note: After you initiate multipart upload and upload\n one or more parts, you must either complete or abort multipart upload in order to stop\n getting charged for storage of the uploaded parts. Only after you either complete or abort\n multipart upload, Amazon S3 frees up the parts storage and stops charging you for the parts\n storage.

\n

For more information on multipart uploads, go to Multipart Upload Overview in the\n Amazon S3 User Guide .

\n

For information on the permissions required to use the multipart upload API, go to\n Multipart Upload and\n Permissions in the Amazon S3 User Guide.

\n

You can optionally request server-side encryption where Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts it for you when you access it. You have\n the option of providing your own encryption key, or you can use the Amazon Web Services managed encryption\n keys. If you choose to provide your own encryption key, the request headers you provide in\n the request must match the headers you used in the request to initiate the upload by using\n CreateMultipartUpload. For more information, go to Using Server-Side Encryption in\n the Amazon S3 User Guide.

\n

Server-side encryption is supported by the S3 Multipart Upload actions. Unless you are\n using a customer-provided encryption key, you don't need to specify the encryption\n parameters in each UploadPart request. Instead, you only need to specify the server-side\n encryption parameters in the initial Initiate Multipart request. For more information, see\n CreateMultipartUpload.

\n

If you requested server-side encryption using a customer-provided encryption key in your\n initiate multipart upload request, you must provide identical encryption information in\n each part upload using the following headers.

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload\n

      \n
    • \n
    • \n

      \n Cause: The specified multipart upload does not exist. The upload\n ID might be invalid, or the multipart upload might have been aborted or\n completed.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found \n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Uploads a part in a multipart upload.

\n \n

In this operation, you provide part data in your request. However, you have an option\n to specify your existing Amazon S3 object as a data source for the part you are uploading. To\n upload a part from an existing object, you use the UploadPartCopy operation.\n

\n
\n

You must initiate a multipart upload (see CreateMultipartUpload)\n before you can upload any part. In response to your initiate request, Amazon S3 returns an\n upload ID, a unique identifier, that you must include in your upload part request.

\n

Part numbers can be any number from 1 to 10,000, inclusive. A part number uniquely\n identifies a part and also defines its position within the object being created. If you\n upload a new part using the same part number that was used with a previous part, the\n previously uploaded part is overwritten.

\n

For information about maximum and minimum part sizes and other multipart upload\n specifications, see Multipart upload limits in the Amazon S3 User Guide.

\n

To ensure that data is not corrupted when traversing the network, specify the\n Content-MD5 header in the upload part request. Amazon S3 checks the part data\n against the provided MD5 value. If they do not match, Amazon S3 returns an error.

\n

If the upload request is signed with Signature Version 4, then Amazon Web Services S3 uses the\n x-amz-content-sha256 header as a checksum instead of\n Content-MD5. For more information see Authenticating\n Requests: Using the Authorization Header (Amazon Web Services Signature Version 4).

\n

\n Note: After you initiate multipart upload and upload\n one or more parts, you must either complete or abort multipart upload in order to stop\n getting charged for storage of the uploaded parts. Only after you either complete or abort\n multipart upload, Amazon S3 frees up the parts storage and stops charging you for the parts\n storage.

\n

For more information on multipart uploads, go to Multipart Upload Overview in the\n Amazon S3 User Guide .

\n

For information on the permissions required to use the multipart upload API, go to\n Multipart\n Upload and Permissions in the Amazon S3 User Guide.

\n

Server-side encryption is for data encryption at rest. Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts it when you access it. You have three\n mutually exclusive options to protect data using server-side encryption in Amazon S3, depending\n on how you choose to manage the encryption keys. Specifically, the encryption key options\n are Amazon S3 managed keys (SSE-S3), Amazon Web Services KMS keys (SSE-KMS), and Customer-Provided Keys\n (SSE-C). Amazon S3 encrypts data with server-side encryption using Amazon S3 managed keys (SSE-S3) by\n default. You can optionally tell Amazon S3 to encrypt data at rest using server-side encryption\n with other key options. The option you use depends on whether you want to use KMS keys\n (SSE-KMS) or provide your own encryption key (SSE-C). If you choose to provide your own\n encryption key, the request headers you provide in the request must match the headers you\n used in the request to initiate the upload by using CreateMultipartUpload.\n For more information, go to Using Server-Side\n Encryption in the Amazon S3 User Guide.

\n

Server-side encryption is supported by the S3 Multipart Upload actions. Unless you are\n using a customer-provided encryption key (SSE-C), you don't need to specify the encryption\n parameters in each UploadPart request. Instead, you only need to specify the server-side\n encryption parameters in the initial Initiate Multipart request. For more information, see\n CreateMultipartUpload.

\n

If you requested server-side encryption using a customer-provided encryption key (SSE-C)\n in your initiate multipart upload request, you must provide identical encryption\n information in each part upload using the following headers.

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

\n UploadPart has the following special errors:

\n
    \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload\n

      \n
    • \n
    • \n

      \n Cause: The specified multipart upload does not exist. The upload\n ID might be invalid, or the multipart upload might have been aborted or\n completed.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found \n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
\n

The following operations are related to UploadPart:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=UploadPart", @@ -33573,7 +35953,7 @@ "target": "com.amazonaws.s3#UploadPartCopyOutput" }, "traits": { - "smithy.api#documentation": "

Uploads a part by copying data from an existing object as data source. You specify the\n data source by adding the request header x-amz-copy-source in your request and\n a byte range by adding the request header x-amz-copy-source-range in your\n request.

\n

For information about maximum and minimum part sizes and other multipart upload specifications, see Multipart upload limits in the Amazon S3 User Guide.

\n \n

Instead of using an existing object as part data, you might use the UploadPart\n action and provide data in your request.

\n
\n

You must initiate a multipart upload before you can upload any part. In response to your\n initiate request. Amazon S3 returns a unique identifier, the upload ID, that you must include in\n your upload part request.

\n

For more information about using the UploadPartCopy operation, see the\n following:

\n
    \n
  • \n

    For conceptual information about multipart uploads, see Uploading Objects Using Multipart\n Upload in the Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about permissions required to use the multipart upload API, see\n Multipart Upload and\n Permissions in the Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about copying objects using a single atomic action vs. a multipart\n upload, see Operations on Objects in\n the Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about using server-side encryption with customer-provided\n encryption keys with the UploadPartCopy operation, see CopyObject and UploadPart.

    \n
  • \n
\n

Note the following additional considerations about the request headers\n x-amz-copy-source-if-match, x-amz-copy-source-if-none-match,\n x-amz-copy-source-if-unmodified-since, and\n x-amz-copy-source-if-modified-since:

\n

\n
    \n
  • \n

    \n Consideration 1 - If both of the\n x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the\n request as follows:

    \n

    \n x-amz-copy-source-if-match condition evaluates to true,\n and;

    \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false;

    \n

    Amazon S3 returns 200 OK and copies the data.\n

    \n
  • \n
  • \n

    \n Consideration 2 - If both of the\n x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the\n request as follows:

    \n

    \n x-amz-copy-source-if-none-match condition evaluates to\n false, and;

    \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true;

    \n

    Amazon S3 returns 412 Precondition Failed response code.\n

    \n
  • \n
\n

\n Versioning\n

\n

If your bucket has versioning enabled, you could have multiple versions of the same\n object. By default, x-amz-copy-source identifies the current version of the\n object to copy. If the current version is a delete marker and you don't specify a versionId\n in the x-amz-copy-source, Amazon S3 returns a 404 error, because the object does\n not exist. If you specify versionId in the x-amz-copy-source and the versionId\n is a delete marker, Amazon S3 returns an HTTP 400 error, because you are not allowed to specify\n a delete marker as a version for the x-amz-copy-source.

\n

You can optionally specify a specific version of the source object to copy by adding the\n versionId subresource as shown in the following example:

\n

\n x-amz-copy-source: /bucket/object?versionId=version id\n

\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload\n

      \n
    • \n
    • \n

      \n Cause: The specified multipart upload does not exist. The upload\n ID might be invalid, or the multipart upload might have been aborted or\n completed.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest\n

      \n
    • \n
    • \n

      \n Cause: The specified copy source is not supported as a byte-range\n copy source.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Uploads a part by copying data from an existing object as data source. You specify the\n data source by adding the request header x-amz-copy-source in your request and\n a byte range by adding the request header x-amz-copy-source-range in your\n request.

\n

For information about maximum and minimum part sizes and other multipart upload\n specifications, see Multipart upload limits in the Amazon S3 User Guide.

\n \n

Instead of using an existing object as part data, you might use the UploadPart\n action and provide data in your request.

\n
\n

You must initiate a multipart upload before you can upload any part. In response to your\n initiate request. Amazon S3 returns a unique identifier, the upload ID, that you must include in\n your upload part request.

\n

For more information about using the UploadPartCopy operation, see the\n following:

\n
    \n
  • \n

    For conceptual information about multipart uploads, see Uploading\n Objects Using Multipart Upload in the\n Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about permissions required to use the multipart upload API, see\n Multipart Upload and Permissions in the\n Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about copying objects using a single atomic action vs. a multipart\n upload, see Operations on Objects in\n the Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about using server-side encryption with customer-provided\n encryption keys with the UploadPartCopy operation, see CopyObject and UploadPart.

    \n
  • \n
\n

Note the following additional considerations about the request headers\n x-amz-copy-source-if-match, x-amz-copy-source-if-none-match,\n x-amz-copy-source-if-unmodified-since, and\n x-amz-copy-source-if-modified-since:

\n

\n
    \n
  • \n

    \n Consideration 1 - If both of the\n x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the\n request as follows:

    \n

    \n x-amz-copy-source-if-match condition evaluates to true,\n and;

    \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false;

    \n

    Amazon S3 returns 200 OK and copies the data.\n

    \n
  • \n
  • \n

    \n Consideration 2 - If both of the\n x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the\n request as follows:

    \n

    \n x-amz-copy-source-if-none-match condition evaluates to\n false, and;

    \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true;

    \n

    Amazon S3 returns 412 Precondition Failed response code.\n

    \n
  • \n
\n
\n
Versioning
\n
\n

If your bucket has versioning enabled, you could have multiple versions of the same\n object. By default, x-amz-copy-source identifies the current version of the\n object to copy. If the current version is a delete marker and you don't specify a versionId\n in the x-amz-copy-source, Amazon S3 returns a 404 error, because the object does\n not exist. If you specify versionId in the x-amz-copy-source and the versionId\n is a delete marker, Amazon S3 returns an HTTP 400 error, because you are not allowed to specify\n a delete marker as a version for the x-amz-copy-source.

\n

You can optionally specify a specific version of the source object to copy by adding the\n versionId subresource as shown in the following example:

\n

\n x-amz-copy-source: /bucket/object?versionId=version id\n

\n
\n
Special errors
\n
\n
    \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload\n

      \n
    • \n
    • \n

      \n Cause: The specified multipart upload does not exist. The upload\n ID might be invalid, or the multipart upload might have been aborted or\n completed.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest\n

      \n
    • \n
    • \n

      \n Cause: The specified copy source is not supported as a byte-range\n copy source.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request\n

      \n
    • \n
    \n
  • \n
\n
\n
\n

The following operations are related to UploadPartCopy:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=UploadPartCopy", @@ -33601,7 +35981,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -33622,7 +36002,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -33630,7 +36010,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -33651,7 +36031,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -33800,7 +36180,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -33856,7 +36236,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -33864,7 +36244,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -33893,7 +36273,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -33919,7 +36299,7 @@ "ChecksumAlgorithm": { "target": "com.amazonaws.s3#ChecksumAlgorithm", "traits": { - "smithy.api#documentation": "

Indicates the algorithm used to create the checksum for the object when using the SDK. This header will not provide any\n additional functionality if not using the SDK. When sending this header, there must be a corresponding x-amz-checksum or\n x-amz-trailer header sent. Otherwise, Amazon S3 fails the request with the HTTP status code 400 Bad Request. For more\n information, see Checking object integrity in\n the Amazon S3 User Guide.

\n

If you provide an individual checksum, Amazon S3 ignores any provided\n ChecksumAlgorithm parameter.

\n

This checksum algorithm must be the same for all parts and it match the checksum\n value supplied in the CreateMultipartUpload request.

", + "smithy.api#documentation": "

Indicates the algorithm used to create the checksum for the object when using the SDK. This header will not provide any\n additional functionality if not using the SDK. When sending this header, there must be a corresponding x-amz-checksum or\n x-amz-trailer header sent. Otherwise, Amazon S3 fails the request with the HTTP status code 400 Bad Request. For more\n information, see Checking object integrity in\n the Amazon S3 User Guide.

\n

If you provide an individual checksum, Amazon S3 ignores any provided\n ChecksumAlgorithm parameter.

\n

This checksum algorithm must be the same for all parts and it match the checksum value\n supplied in the CreateMultipartUpload request.

", "smithy.api#httpHeader": "x-amz-sdk-checksum-algorithm" } }, @@ -34105,7 +36485,7 @@ "smithy.api#auth": [ "aws.auth#sigv4" ], - "smithy.api#documentation": "

Passes transformed\n objects to a GetObject operation when using Object Lambda access points. For information about\n Object Lambda access points, see Transforming objects with\n Object Lambda access points in the Amazon S3 User Guide.

\n

This operation supports metadata that can be returned by GetObject, in addition to\n RequestRoute, RequestToken, StatusCode,\n ErrorCode, and ErrorMessage. The GetObject\n response metadata is supported so that the WriteGetObjectResponse caller,\n typically an Lambda function, can provide the same metadata when it internally invokes\n GetObject. When WriteGetObjectResponse is called by a\n customer-owned Lambda function, the metadata returned to the end user\n GetObject call might differ from what Amazon S3 would normally return.

\n

You can include any number of metadata headers. When including a metadata header, it should be\n prefaced with x-amz-meta. For example, x-amz-meta-my-custom-header: MyCustomValue.\n The primary use case for this is to forward GetObject metadata.

\n

Amazon Web Services provides some prebuilt Lambda functions that you can use with S3 Object Lambda to detect and redact\n personally identifiable information (PII) and decompress S3 objects. These Lambda functions\n are available in the Amazon Web Services Serverless Application Repository, and can be selected through the Amazon Web Services Management Console when you create your\n Object Lambda access point.

\n

Example 1: PII Access Control - This Lambda function uses Amazon Comprehend, a natural language processing (NLP) service using machine learning to find insights and relationships in text. It automatically detects personally identifiable information (PII) such as names, addresses, dates, credit card numbers, and social security numbers from documents in your Amazon S3 bucket.

\n

Example 2: PII Redaction - This Lambda function uses Amazon Comprehend, a natural language processing (NLP) service using machine learning to find insights and relationships in text. It automatically redacts personally identifiable information (PII) such as names, addresses, dates, credit card numbers, and social security numbers from documents in your Amazon S3 bucket.

\n

Example 3: Decompression - The Lambda function S3ObjectLambdaDecompression, is equipped to decompress objects stored in S3 in one of six compressed file formats including bzip2, gzip, snappy, zlib, zstandard and ZIP.

\n

For information on how to view and use these functions, see Using Amazon Web Services built Lambda functions in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Passes transformed objects to a GetObject operation when using Object Lambda access points. For\n information about Object Lambda access points, see Transforming objects with\n Object Lambda access points in the Amazon S3 User Guide.

\n

This operation supports metadata that can be returned by GetObject, in addition to\n RequestRoute, RequestToken, StatusCode,\n ErrorCode, and ErrorMessage. The GetObject\n response metadata is supported so that the WriteGetObjectResponse caller,\n typically an Lambda function, can provide the same metadata when it internally invokes\n GetObject. When WriteGetObjectResponse is called by a\n customer-owned Lambda function, the metadata returned to the end user\n GetObject call might differ from what Amazon S3 would normally return.

\n

You can include any number of metadata headers. When including a metadata header, it\n should be prefaced with x-amz-meta. For example,\n x-amz-meta-my-custom-header: MyCustomValue. The primary use case for this\n is to forward GetObject metadata.

\n

Amazon Web Services provides some prebuilt Lambda functions that you can use with S3 Object Lambda to\n detect and redact personally identifiable information (PII) and decompress S3 objects.\n These Lambda functions are available in the Amazon Web Services Serverless Application Repository, and\n can be selected through the Amazon Web Services Management Console when you create your Object Lambda access point.

\n

Example 1: PII Access Control - This Lambda function uses Amazon Comprehend, a\n natural language processing (NLP) service using machine learning to find insights and\n relationships in text. It automatically detects personally identifiable information (PII)\n such as names, addresses, dates, credit card numbers, and social security numbers from\n documents in your Amazon S3 bucket.

\n

Example 2: PII Redaction - This Lambda function uses Amazon Comprehend, a natural\n language processing (NLP) service using machine learning to find insights and relationships\n in text. It automatically redacts personally identifiable information (PII) such as names,\n addresses, dates, credit card numbers, and social security numbers from documents in your\n Amazon S3 bucket.

\n

Example 3: Decompression - The Lambda function S3ObjectLambdaDecompression, is\n equipped to decompress objects stored in S3 in one of six compressed file formats including\n bzip2, gzip, snappy, zlib, zstandard and ZIP.

\n

For information on how to view and use these functions, see Using Amazon Web Services built Lambda\n functions in the Amazon S3 User Guide.

", "smithy.api#endpoint": { "hostPrefix": "{RequestRoute}." }, @@ -34153,7 +36533,7 @@ "target": "com.amazonaws.s3#GetObjectResponseStatusCode", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The integer status code for an HTTP response of a corresponding GetObject\n request.

\n

\n Status Codes\n

\n
    \n
  • \n

    \n 200 - OK\n

    \n
  • \n
  • \n

    \n 206 - Partial Content\n

    \n
  • \n
  • \n

    \n 304 - Not Modified\n

    \n
  • \n
  • \n

    \n 400 - Bad Request\n

    \n
  • \n
  • \n

    \n 401 - Unauthorized\n

    \n
  • \n
  • \n

    \n 403 - Forbidden\n

    \n
  • \n
  • \n

    \n 404 - Not Found\n

    \n
  • \n
  • \n

    \n 405 - Method Not Allowed\n

    \n
  • \n
  • \n

    \n 409 - Conflict\n

    \n
  • \n
  • \n

    \n 411 - Length Required\n

    \n
  • \n
  • \n

    \n 412 - Precondition Failed\n

    \n
  • \n
  • \n

    \n 416 - Range Not Satisfiable\n

    \n
  • \n
  • \n

    \n 500 - Internal Server Error\n

    \n
  • \n
  • \n

    \n 503 - Service Unavailable\n

    \n
  • \n
", + "smithy.api#documentation": "

The integer status code for an HTTP response of a corresponding GetObject\n request. The following is a list of status codes.

\n
    \n
  • \n

    \n 200 - OK\n

    \n
  • \n
  • \n

    \n 206 - Partial Content\n

    \n
  • \n
  • \n

    \n 304 - Not Modified\n

    \n
  • \n
  • \n

    \n 400 - Bad Request\n

    \n
  • \n
  • \n

    \n 401 - Unauthorized\n

    \n
  • \n
  • \n

    \n 403 - Forbidden\n

    \n
  • \n
  • \n

    \n 404 - Not Found\n

    \n
  • \n
  • \n

    \n 405 - Method Not Allowed\n

    \n
  • \n
  • \n

    \n 409 - Conflict\n

    \n
  • \n
  • \n

    \n 411 - Length Required\n

    \n
  • \n
  • \n

    \n 412 - Precondition Failed\n

    \n
  • \n
  • \n

    \n 416 - Range Not Satisfiable\n

    \n
  • \n
  • \n

    \n 500 - Internal Server Error\n

    \n
  • \n
  • \n

    \n 503 - Service Unavailable\n

    \n
  • \n
", "smithy.api#httpHeader": "x-amz-fwd-status" } }, @@ -34195,7 +36575,7 @@ "ContentEncoding": { "target": "com.amazonaws.s3#ContentEncoding", "traits": { - "smithy.api#documentation": "

Specifies what content encodings have been applied to the object and thus what decoding\n mechanisms must be applied to obtain the media-type referenced by the Content-Type header\n field.

", + "smithy.api#documentation": "

Specifies what content encodings have been applied to the object and thus what decoding\n mechanisms must be applied to obtain the media-type referenced by the Content-Type header\n field.

", "smithy.api#httpHeader": "x-amz-fwd-header-Content-Encoding" } }, @@ -34231,28 +36611,28 @@ "ChecksumCRC32": { "target": "com.amazonaws.s3#ChecksumCRC32", "traits": { - "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is the\n same data that was originally sent. This specifies the base64-encoded, 32-bit CRC32 checksum\n of the object returned by the Object Lambda function. This may not match the checksum for the\n object stored in Amazon S3. Amazon S3 will perform validation of the checksum values only when the original\n GetObject request required checksum validation. For more information about checksums, see\n Checking\n object integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple\n checksum headers, this request will fail.

\n

", + "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is\n the same data that was originally sent. This specifies the base64-encoded, 32-bit CRC32\n checksum of the object returned by the Object Lambda function. This may not match the\n checksum for the object stored in Amazon S3. Amazon S3 will perform validation of the checksum values\n only when the original GetObject request required checksum validation. For\n more information about checksums, see Checking object\n integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple checksum\n headers, this request will fail.

\n

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-checksum-crc32" } }, "ChecksumCRC32C": { "target": "com.amazonaws.s3#ChecksumCRC32C", "traits": { - "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is the\n same data that was originally sent. This specifies the base64-encoded, 32-bit CRC32C checksum\n of the object returned by the Object Lambda function. This may not match the checksum for the\n object stored in Amazon S3. Amazon S3 will perform validation of the checksum values only when the original\n GetObject request required checksum validation. For more information about checksums, see\n Checking\n object integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple\n checksum headers, this request will fail.

", + "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is\n the same data that was originally sent. This specifies the base64-encoded, 32-bit CRC32C\n checksum of the object returned by the Object Lambda function. This may not match the\n checksum for the object stored in Amazon S3. Amazon S3 will perform validation of the checksum values\n only when the original GetObject request required checksum validation. For\n more information about checksums, see Checking object\n integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple checksum\n headers, this request will fail.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-checksum-crc32c" } }, "ChecksumSHA1": { "target": "com.amazonaws.s3#ChecksumSHA1", "traits": { - "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is the\n same data that was originally sent. This specifies the base64-encoded, 160-bit SHA-1 digest\n of the object returned by the Object Lambda function. This may not match the checksum for the\n object stored in Amazon S3. Amazon S3 will perform validation of the checksum values only when the original\n GetObject request required checksum validation. For more information about checksums, see\n Checking\n object integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple\n checksum headers, this request will fail.

", + "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is\n the same data that was originally sent. This specifies the base64-encoded, 160-bit SHA-1\n digest of the object returned by the Object Lambda function. This may not match the\n checksum for the object stored in Amazon S3. Amazon S3 will perform validation of the checksum values\n only when the original GetObject request required checksum validation. For\n more information about checksums, see Checking object\n integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple checksum\n headers, this request will fail.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-checksum-sha1" } }, "ChecksumSHA256": { "target": "com.amazonaws.s3#ChecksumSHA256", "traits": { - "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is the\n same data that was originally sent. This specifies the base64-encoded, 256-bit SHA-256 digest\n of the object returned by the Object Lambda function. This may not match the checksum for the\n object stored in Amazon S3. Amazon S3 will perform validation of the checksum values only when the original\n GetObject request required checksum validation. For more information about checksums, see\n Checking\n object integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple\n checksum headers, this request will fail.

", + "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is\n the same data that was originally sent. This specifies the base64-encoded, 256-bit SHA-256\n digest of the object returned by the Object Lambda function. This may not match the\n checksum for the object stored in Amazon S3. Amazon S3 will perform validation of the checksum values\n only when the original GetObject request required checksum validation. For\n more information about checksums, see Checking object\n integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple checksum\n headers, this request will fail.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-checksum-sha256" } }, @@ -34281,7 +36661,7 @@ "Expiration": { "target": "com.amazonaws.s3#Expiration", "traits": { - "smithy.api#documentation": "

If the object expiration is configured (see PUT Bucket lifecycle), the response\n includes this header. It includes the expiry-date and rule-id\n key-value pairs that provide the object expiration information. The value of the\n rule-id is URL-encoded.

", + "smithy.api#documentation": "

If the object expiration is configured (see PUT Bucket lifecycle), the response includes\n this header. It includes the expiry-date and rule-id key-value\n pairs that provide the object expiration information. The value of the rule-id\n is URL-encoded.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-expiration" } }, @@ -34310,7 +36690,7 @@ "ObjectLockMode": { "target": "com.amazonaws.s3#ObjectLockMode", "traits": { - "smithy.api#documentation": "

Indicates whether an object stored in Amazon S3 has Object Lock enabled. For more\n information about S3 Object Lock, see Object Lock.

", + "smithy.api#documentation": "

Indicates whether an object stored in Amazon S3 has Object Lock enabled. For more information\n about S3 Object Lock, see Object Lock.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-object-lock-mode" } }, @@ -34339,7 +36719,7 @@ "ReplicationStatus": { "target": "com.amazonaws.s3#ReplicationStatus", "traits": { - "smithy.api#documentation": "

Indicates if request involves bucket that is either a source or destination in a Replication rule. For more\n information about S3 Replication, see Replication.

", + "smithy.api#documentation": "

Indicates if request involves bucket that is either a source or destination in a\n Replication rule. For more information about S3 Replication, see Replication.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-replication-status" } }, @@ -34352,28 +36732,28 @@ "Restore": { "target": "com.amazonaws.s3#Restore", "traits": { - "smithy.api#documentation": "

Provides information about object restoration operation and expiration time of the\n restored object copy.

", + "smithy.api#documentation": "

Provides information about object restoration operation and expiration time of the\n restored object copy.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-restore" } }, "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing requested object in Amazon S3 (for example, AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing requested object in Amazon S3 (for\n example, AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-server-side-encryption" } }, "SSECustomerAlgorithm": { "target": "com.amazonaws.s3#SSECustomerAlgorithm", "traits": { - "smithy.api#documentation": "

Encryption algorithm used if server-side encryption with a customer-provided encryption key was specified for object stored in Amazon S3.

", + "smithy.api#documentation": "

Encryption algorithm used if server-side encryption with a customer-provided encryption\n key was specified for object stored in Amazon S3.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-server-side-encryption-customer-algorithm" } }, "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric customer managed key that was used for stored in Amazon S3 object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for stored in Amazon S3 object.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -34387,7 +36767,7 @@ "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

Provides storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage\n Classes.

", + "smithy.api#documentation": "

Provides storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage Classes.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-storage-class" } }, diff --git a/aws/sdk/aws-models/s3control.json b/aws/sdk/aws-models/s3control.json index c47218c753..2944356a96 100644 --- a/aws/sdk/aws-models/s3control.json +++ b/aws/sdk/aws-models/s3control.json @@ -332,6 +332,120 @@ "conditions": [], "type": "tree", "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "snow" + ] + }, + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + }, + { + "fn": "parseURL", + "argv": [ + { + "ref": "Endpoint" + } + ], + "assign": "url" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "partitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "S3 Snow does not support Dual-stack", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "S3 Snow does not support FIPS", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": "{url#scheme}://{url#authority}", + "properties": { + "authSchemes": [ + { + "disableDoubleEncoding": true, + "name": "sigv4", + "signingName": "s3", + "signingRegion": "{Region}" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "A valid partition could not be determined", + "type": "error" + } + ] + }, { "conditions": [ { @@ -6060,6 +6174,133 @@ "UseDualStack": false, "UseFIPS": false } + }, + { + "documentation": "S3 Snow Control with bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://10.0.1.12:433" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "S3 Snow Control without bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://10.0.1.12:433" + } + }, + "params": { + "Region": "snow", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "S3 Snow Control with bucket and without port", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://10.0.1.12" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://10.0.1.12", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "S3 Snow Control with bucket and with DNS", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://s3snow.com" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "http://s3snow.com", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "S3 Snow Control with FIPS enabled", + "expect": { + "error": "S3 Snow does not support FIPS" + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": true, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow Control with Dual-stack enabled", + "expect": { + "error": "S3 Snow does not support Dual-stack" + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": true, + "Accelerate": false + } } ], "version": "1.0" @@ -6810,6 +7051,12 @@ "traits": { "smithy.api#documentation": "

Specifies the ARN for the Object Lambda Access Point.

" } + }, + "Alias": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAlias", + "traits": { + "smithy.api#documentation": "

The alias of the Object Lambda Access Point.

" + } } } }, @@ -7651,7 +7898,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "\n

This operation deletes an Amazon S3 on Outposts bucket's replication configuration. To\n delete an S3 bucket's replication configuration, see DeleteBucketReplication in the Amazon S3 API Reference.

\n
\n

Deletes the replication configuration from the specified S3 on Outposts bucket.

\n

To use this operation, you must have permissions to perform the\n s3-outposts:PutReplicationConfiguration action. The Outposts bucket owner\n has this permission by default and can grant it to others. For more information about\n permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts buckets in the Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

For information about S3 replication on Outposts configuration, see Replicating objects for Amazon Web Services Outposts in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", + "smithy.api#documentation": "\n

This operation deletes an Amazon S3 on Outposts bucket's replication configuration. To\n delete an S3 bucket's replication configuration, see DeleteBucketReplication in the Amazon S3 API Reference.

\n
\n

Deletes the replication configuration from the specified S3 on Outposts bucket.

\n

To use this operation, you must have permissions to perform the\n s3-outposts:PutReplicationConfiguration action. The Outposts bucket owner\n has this permission by default and can grant it to others. For more information about\n permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts buckets in the Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

For information about S3 replication on Outposts configuration, see Replicating objects for S3 on Outposts in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -8679,6 +8926,12 @@ "traits": { "smithy.api#documentation": "

The date and time when the specified Object Lambda Access Point was created.

" } + }, + "Alias": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAlias", + "traits": { + "smithy.api#documentation": "

The alias of the Object Lambda Access Point.

" + } } } }, @@ -9202,7 +9455,7 @@ "target": "com.amazonaws.s3control#GetBucketReplicationResult" }, "traits": { - "smithy.api#documentation": "\n

This operation gets an Amazon S3 on Outposts bucket's replication configuration. To get an\n S3 bucket's replication configuration, see GetBucketReplication\n in the Amazon S3 API Reference.

\n
\n

Returns the replication configuration of an S3 on Outposts bucket. For more information\n about S3 on Outposts, see Using Amazon S3 on Outposts in the\n Amazon S3 User Guide. For information about S3 replication on Outposts\n configuration, see Replicating objects for Amazon Web Services\n Outposts in the Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

This action requires permissions for the\n s3-outposts:GetReplicationConfiguration action. The Outposts bucket owner\n has this permission by default and can grant it to others. For more information about\n permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts bucket in the Amazon S3 User Guide.

\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

If you include the Filter element in a replication configuration, you must\n also include the DeleteMarkerReplication, Status, and\n Priority elements. The response also returns those elements.

\n

For information about S3 on Outposts replication failure reasons, see Replication failure reasons in the Amazon S3 User Guide.

\n

The following operations are related to GetBucketReplication:

\n ", + "smithy.api#documentation": "\n

This operation gets an Amazon S3 on Outposts bucket's replication configuration. To get an\n S3 bucket's replication configuration, see GetBucketReplication\n in the Amazon S3 API Reference.

\n
\n

Returns the replication configuration of an S3 on Outposts bucket. For more information\n about S3 on Outposts, see Using Amazon S3 on Outposts in the\n Amazon S3 User Guide. For information about S3 replication on Outposts\n configuration, see Replicating objects for S3 on Outposts in the Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

This action requires permissions for the\n s3-outposts:GetReplicationConfiguration action. The Outposts bucket owner\n has this permission by default and can grant it to others. For more information about\n permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts bucket in the Amazon S3 User Guide.

\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

If you include the Filter element in a replication configuration, you must\n also include the DeleteMarkerReplication, Status, and\n Priority elements. The response also returns those elements.

\n

For information about S3 on Outposts replication failure reasons, see Replication failure reasons in the Amazon S3 User Guide.

\n

The following operations are related to GetBucketReplication:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -10968,7 +11221,7 @@ "AbortIncompleteMultipartUpload": { "target": "com.amazonaws.s3control#AbortIncompleteMultipartUpload", "traits": { - "smithy.api#documentation": "

Specifies the days since the initiation of an incomplete multipart upload that Amazon S3\n waits before permanently removing all parts of the upload. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies the days since the initiation of an incomplete multipart upload that Amazon S3\n waits before permanently removing all parts of the upload. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration in the\n Amazon S3 User Guide.

" } } }, @@ -12116,12 +12369,71 @@ "traits": { "smithy.api#documentation": "

Specifies the ARN for the Object Lambda Access Point.

" } + }, + "Alias": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAlias", + "traits": { + "smithy.api#documentation": "

The alias of the Object Lambda Access Point.

" + } } }, "traits": { "smithy.api#documentation": "

An access point with an attached Lambda function used to access transformed data from an Amazon S3\n bucket.

" } }, + "com.amazonaws.s3control#ObjectLambdaAccessPointAlias": { + "type": "structure", + "members": { + "Value": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAliasValue", + "traits": { + "smithy.api#documentation": "

The alias value of the Object Lambda Access Point.

" + } + }, + "Status": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAliasStatus", + "traits": { + "smithy.api#documentation": "

The status of the Object Lambda Access Point alias. If the status is PROVISIONING, the Object Lambda Access Point is provisioning the alias and the alias is not ready for use yet. If \n the status is READY, the Object Lambda Access Point alias is successfully provisioned and ready for use.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The alias of an Object Lambda Access Point. For more information, see How to use a bucket-style alias for your S3 bucket\n Object Lambda Access Point.

" + } + }, + "com.amazonaws.s3control#ObjectLambdaAccessPointAliasStatus": { + "type": "enum", + "members": { + "PROVISIONING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PROVISIONING" + } + }, + "READY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "READY" + } + } + }, + "traits": { + "smithy.api#length": { + "min": 2, + "max": 16 + } + } + }, + "com.amazonaws.s3control#ObjectLambdaAccessPointAliasValue": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 3, + "max": 63 + }, + "smithy.api#pattern": "^[0-9a-z\\\\-]{3,63}$" + } + }, "com.amazonaws.s3control#ObjectLambdaAccessPointArn": { "type": "string", "traits": { @@ -12869,7 +13181,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "\n

This action creates an Amazon S3 on Outposts bucket's replication configuration. To create\n an S3 bucket's replication configuration, see PutBucketReplication\n in the Amazon S3 API Reference.

\n
\n

Creates a replication configuration or replaces an existing one. For information about\n S3 replication on Outposts configuration, see Replicating objects for Amazon Web Services Outposts in the\n Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the following information:

\n
    \n
  • \n

    The name of the destination bucket or buckets where you want S3 on Outposts to\n replicate objects

    \n
  • \n
  • \n

    The Identity and Access Management (IAM) role that S3 on Outposts can assume to replicate objects on\n your behalf

    \n
  • \n
  • \n

    Other relevant information, such as replication rules

    \n
  • \n
\n

A replication configuration must include at least one rule and can contain a maximum of\n 100. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source Outposts bucket. To choose additional subsets of objects to replicate, add a\n rule for each subset.

\n

To specify a subset of the objects in the source Outposts bucket to apply a replication\n rule to, add the Filter element as a child of the Rule element.\n You can filter objects based on an object key prefix, one or more object tags, or both.\n When you add the Filter element in the configuration, you must also add the\n following elements: DeleteMarkerReplication, Status, and\n Priority.

\n

Using PutBucketReplication on Outposts requires that both the source and\n destination buckets must have versioning enabled. For information about enabling versioning\n on a bucket, see Managing S3 Versioning\n for your S3 on Outposts bucket.

\n

For information about S3 on Outposts replication failure reasons, see Replication failure reasons in the Amazon S3 User Guide.

\n

\n Handling Replication of Encrypted Objects\n

\n

Outposts buckets are encrypted at all times. All the objects in the source Outposts\n bucket are encrypted and can be replicated. Also, all the replicas in the destination\n Outposts bucket are encrypted with the same encryption key as the objects in the source\n Outposts bucket.

\n

\n Permissions\n

\n

To create a PutBucketReplication request, you must have\n s3-outposts:PutReplicationConfiguration permissions for the bucket. The\n Outposts bucket owner has this permission by default and can grant it to others. For more\n information about permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts buckets.

\n \n

To perform this operation, the user or role must also have the iam:PassRole permission.

\n
\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

The following operations are related to PutBucketReplication:

\n ", + "smithy.api#documentation": "\n

This action creates an Amazon S3 on Outposts bucket's replication configuration. To create\n an S3 bucket's replication configuration, see PutBucketReplication\n in the Amazon S3 API Reference.

\n
\n

Creates a replication configuration or replaces an existing one. For information about\n S3 replication on Outposts configuration, see Replicating objects for S3 on Outposts in the\n Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the following information:

\n
    \n
  • \n

    The name of the destination bucket or buckets where you want S3 on Outposts to\n replicate objects

    \n
  • \n
  • \n

    The Identity and Access Management (IAM) role that S3 on Outposts can assume to replicate objects on\n your behalf

    \n
  • \n
  • \n

    Other relevant information, such as replication rules

    \n
  • \n
\n

A replication configuration must include at least one rule and can contain a maximum of\n 100. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source Outposts bucket. To choose additional subsets of objects to replicate, add a\n rule for each subset.

\n

To specify a subset of the objects in the source Outposts bucket to apply a replication\n rule to, add the Filter element as a child of the Rule element.\n You can filter objects based on an object key prefix, one or more object tags, or both.\n When you add the Filter element in the configuration, you must also add the\n following elements: DeleteMarkerReplication, Status, and\n Priority.

\n

Using PutBucketReplication on Outposts requires that both the source and\n destination buckets must have versioning enabled. For information about enabling versioning\n on a bucket, see Managing S3 Versioning\n for your S3 on Outposts bucket.

\n

For information about S3 on Outposts replication failure reasons, see Replication failure reasons in the Amazon S3 User Guide.

\n

\n Handling Replication of Encrypted Objects\n

\n

Outposts buckets are encrypted at all times. All the objects in the source Outposts\n bucket are encrypted and can be replicated. Also, all the replicas in the destination\n Outposts bucket are encrypted with the same encryption key as the objects in the source\n Outposts bucket.

\n

\n Permissions\n

\n

To create a PutBucketReplication request, you must have\n s3-outposts:PutReplicationConfiguration permissions for the bucket. The\n Outposts bucket owner has this permission by default and can grant it to others. For more\n information about permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts buckets.

\n \n

To perform this operation, the user or role must also have the iam:CreateRole and iam:PassRole permissions. \n For more information, see Granting a user\n permissions to pass a role to an Amazon Web Services service.

\n
\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

The following operations are related to PutBucketReplication:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -13001,7 +13313,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "\n

This operation sets the versioning state\n for\n S3 on Outposts\n buckets\n only. To set the versioning state for an S3 bucket, see PutBucketVersioning in the Amazon S3 API Reference.

\n
\n

Sets the versioning state for an S3 on Outposts bucket. With\n S3\n Versioning,\n you can save multiple distinct copies of your\n objects\n and recover from unintended user actions and application failures.

\n

You can set the versioning state to one of the following:

\n
    \n
  • \n

    \n Enabled - Enables versioning for the objects in\n the bucket. All objects added to the bucket receive a unique version ID.

    \n
  • \n
  • \n

    \n Suspended - Suspends versioning for the objects\n in the bucket. All objects added to the bucket receive the version ID\n null.

    \n
  • \n
\n

If you've never set versioning on your bucket, it has no versioning state. In that case,\n a \n GetBucketVersioning request does not return a versioning state value.

\n

When you enable S3 Versioning, for each object in your bucket, you have a current\n version and zero or more noncurrent versions. You can configure your bucket S3 Lifecycle\n rules to expire noncurrent versions after a specified time period. For more information,\n see Creating and managing\n a lifecycle configuration for your S3 on Outposts bucket in the Amazon S3\n User Guide.

\n

If you have an object expiration lifecycle policy in your non-versioned bucket and you\n want to maintain the same permanent delete behavior when you enable versioning, you must\n add a noncurrent expiration policy. The noncurrent expiration lifecycle policy will manage\n the\n deletions\n of the noncurrent object versions in the version-enabled bucket. For more information, see\n Versioning in the Amazon S3 User Guide.

\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

The following operations are related to PutBucketVersioning for\n S3 on Outposts.

\n ", + "smithy.api#documentation": "\n

This operation sets the versioning state\n for\n S3 on Outposts\n buckets\n only. To set the versioning state for an S3 bucket, see PutBucketVersioning in the Amazon S3 API Reference.

\n
\n

Sets the versioning state for an S3 on Outposts bucket. With\n S3\n Versioning,\n you can save multiple distinct copies of your\n objects\n and recover from unintended user actions and application failures.

\n

You can set the versioning state to one of the following:

\n
    \n
  • \n

    \n Enabled - Enables versioning for the objects in\n the bucket. All objects added to the bucket receive a unique version ID.

    \n
  • \n
  • \n

    \n Suspended - Suspends versioning for the objects\n in the bucket. All objects added to the bucket receive the version ID\n null.

    \n
  • \n
\n

If you've never set versioning on your bucket, it has no versioning state. In that case,\n a \n GetBucketVersioning request does not return a versioning state value.

\n

When you enable S3 Versioning, for each object in your bucket, you have a current\n version and zero or more noncurrent versions. You can configure your bucket S3 Lifecycle\n rules to expire noncurrent versions after a specified time period. For more information,\n see Creating and managing\n a lifecycle configuration for your S3 on Outposts bucket in the Amazon S3\n User Guide.

\n

If you have an object expiration lifecycle configuration in your non-versioned bucket and you\n want to maintain the same permanent delete behavior when you enable versioning, you must\n add a noncurrent expiration policy. The noncurrent expiration lifecycle configuration will manage\n the deletes of the noncurrent object versions in the version-enabled bucket. For more\n information, see Versioning in the Amazon S3\n User Guide.

\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

The following operations are related to PutBucketVersioning for\n S3 on Outposts.

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -13625,7 +13937,7 @@ "target": "com.amazonaws.s3control#Priority", "traits": { "smithy.api#default": null, - "smithy.api#documentation": "

The priority indicates which rule has precedence whenever two or more replication rules\n conflict. S3 on Outposts attempts to replicate objects according to all replication rules.\n However, if there are two or more rules with the same destination Outposts bucket, then objects will\n be replicated according to the rule with the highest priority. The higher the number, the\n higher the priority.

\n

For more information, see Creating replication rules between Outposts in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

The priority indicates which rule has precedence whenever two or more replication rules\n conflict. S3 on Outposts attempts to replicate objects according to all replication rules.\n However, if there are two or more rules with the same destination Outposts bucket, then objects will\n be replicated according to the rule with the highest priority. The higher the number, the\n higher the priority.

\n

For more information, see Creating replication rules on Outposts in the\n Amazon S3 User Guide.

" } }, "Prefix": { @@ -15273,7 +15585,7 @@ "target": "com.amazonaws.s3control#SubmitMultiRegionAccessPointRoutesResult" }, "traits": { - "smithy.api#documentation": "

Submits an updated route configuration for a Multi-Region Access Point. This API operation updates the\n routing status for the specified Regions from active to passive, or from passive to active.\n A value of 0 indicates a passive status, which means that traffic won't be\n routed to the specified Region. A value of 100 indicates an active status,\n which means that traffic will be routed to the specified Region. At least one Region must be active at all times.

\n

When\n the routing configuration is changed, any in-progress operations (uploads, copies, deletes,\n and so on) to formerly active Regions will continue to run to their\n final completion state (success or failure). The routing configurations of any Regions that\n aren’t specified remain unchanged.

\n \n

Updated routing configurations might not be immediately applied.\n It\n can take up to 2 minutes for your changes to take effect.

\n
\n

To submit routing control changes and failover requests, use the Amazon S3 failover control\n infrastructure endpoints in these five Amazon Web Services Regions:

\n
    \n
  • \n

    \n us-east-1\n

    \n
  • \n
  • \n

    \n us-west-2\n

    \n
  • \n
  • \n

    \n ap-southeast-2\n

    \n
  • \n
  • \n

    \n ap-northeast-1\n

    \n
  • \n
  • \n

    \n eu-west-1\n

    \n
  • \n
\n \n

Your Amazon S3 bucket does not need to be in these five Regions.

\n
", + "smithy.api#documentation": "

Submits an updated route configuration for a Multi-Region Access Point. This API operation updates the\n routing status for the specified Regions from active to passive, or from passive to active.\n A value of 0 indicates a passive status, which means that traffic won't be\n routed to the specified Region. A value of 100 indicates an active status,\n which means that traffic will be routed to the specified Region. At least one Region must be active at all times.

\n

When the routing configuration is changed, any in-progress operations (uploads, copies,\n deletes, and so on) to formerly active Regions will continue to run to their final\n completion state (success or failure). The routing configurations of any Regions that\n aren’t specified remain unchanged.

\n \n

Updated routing configurations might not be immediately applied. It can take up to 2\n minutes for your changes to take effect.

\n
\n

To submit routing control changes and failover requests, use the Amazon S3 failover control\n infrastructure endpoints in these five Amazon Web Services Regions:

\n
    \n
  • \n

    \n us-east-1\n

    \n
  • \n
  • \n

    \n us-west-2\n

    \n
  • \n
  • \n

    \n ap-southeast-2\n

    \n
  • \n
  • \n

    \n ap-northeast-1\n

    \n
  • \n
  • \n

    \n eu-west-1\n

    \n
  • \n
\n \n

Your Amazon S3 bucket does not need to be in these five Regions.

\n
", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, diff --git a/aws/sdk/aws-models/sdk-endpoints.json b/aws/sdk/aws-models/sdk-endpoints.json index c57d7fd0ff..b74e1ac35d 100644 --- a/aws/sdk/aws-models/sdk-endpoints.json +++ b/aws/sdk/aws-models/sdk-endpoints.json @@ -41,6 +41,9 @@ "ap-south-1" : { "description" : "Asia Pacific (Mumbai)" }, + "ap-south-2" : { + "description" : "Asia Pacific (Hyderabad)" + }, "ap-southeast-1" : { "description" : "Asia Pacific (Singapore)" }, @@ -50,18 +53,27 @@ "ap-southeast-3" : { "description" : "Asia Pacific (Jakarta)" }, + "ap-southeast-4" : { + "description" : "Asia Pacific (Melbourne)" + }, "ca-central-1" : { "description" : "Canada (Central)" }, "eu-central-1" : { "description" : "Europe (Frankfurt)" }, + "eu-central-2" : { + "description" : "Europe (Zurich)" + }, "eu-north-1" : { "description" : "Europe (Stockholm)" }, "eu-south-1" : { "description" : "Europe (Milan)" }, + "eu-south-2" : { + "description" : "Europe (Spain)" + }, "eu-west-1" : { "description" : "Europe (Ireland)" }, @@ -107,9 +119,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "access-analyzer-fips.ca-central-1.amazonaws.com", @@ -117,8 +131,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -206,9 +222,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "acm-fips.ca-central-1.amazonaws.com", @@ -223,8 +241,10 @@ "hostname" : "acm-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -296,9 +316,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "acm-pca-fips.ca-central-1.amazonaws.com", @@ -306,8 +328,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -346,6 +370,7 @@ "deprecated" : true, "hostname" : "acm-pca-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -426,6 +451,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -447,6 +473,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -458,6 +485,18 @@ "us-west-2" : { } } }, + "aoss" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, "api.detective" : { "defaults" : { "protocols" : [ "https" ] @@ -577,6 +616,12 @@ }, "hostname" : "api.ecr.ap-south-1.amazonaws.com" }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "api.ecr.ap-south-2.amazonaws.com" + }, "ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -595,6 +640,12 @@ }, "hostname" : "api.ecr.ap-southeast-3.amazonaws.com" }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "api.ecr.ap-southeast-4.amazonaws.com" + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -647,6 +698,12 @@ }, "hostname" : "api.ecr.eu-central-1.amazonaws.com" }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "api.ecr.eu-central-2.amazonaws.com" + }, "eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -659,6 +716,12 @@ }, "hostname" : "api.ecr.eu-south-1.amazonaws.com" }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "api.ecr.eu-south-2.amazonaws.com" + }, "eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -793,6 +856,22 @@ } } }, + "api.ecr-public" : { + "endpoints" : { + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.ecr-public.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.ecr-public.us-west-2.amazonaws.com" + } + } + }, "api.elastic-inference" : { "endpoints" : { "ap-northeast-1" : { @@ -956,12 +1035,15 @@ }, "api.mediatailor" : { "endpoints" : { + "af-south-1" : { }, "ap-northeast-1" : { }, + "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "eu-central-1" : { }, "eu-west-1" : { }, "us-east-1" : { }, + "us-east-2" : { }, "us-west-2" : { } } }, @@ -990,16 +1072,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -1116,6 +1203,7 @@ "deprecated" : true, "hostname" : "api.tunneling.iot-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -1152,9 +1240,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "apigateway-fips.ca-central-1.amazonaws.com", @@ -1162,8 +1252,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -1253,13 +1345,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -1280,15 +1376,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -1328,13 +1430,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -1357,6 +1463,7 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, @@ -1576,6 +1683,9 @@ "apprunner" : { "endpoints" : { "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, "eu-west-1" : { }, "fips-us-east-1" : { "credentialScope" : { @@ -1642,6 +1752,7 @@ "deprecated" : true, "hostname" : "appstream2-fips.us-west-2.amazonaws.com" }, + "sa-east-1" : { }, "us-east-1" : { "variants" : [ { "hostname" : "appstream2-fips.us-east-1.amazonaws.com", @@ -1679,16 +1790,20 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -1703,14 +1818,49 @@ }, "endpoints" : { "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, + "us-west-1" : { }, "us-west-2" : { } } }, @@ -1856,6 +2006,9 @@ "variants" : [ { "hostname" : "athena-fips.us-east-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] }, { "hostname" : "athena.us-east-1.api.aws", "tags" : [ "dualstack" ] @@ -1865,6 +2018,9 @@ "variants" : [ { "hostname" : "athena-fips.us-east-2.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] }, { "hostname" : "athena.us-east-2.api.aws", "tags" : [ "dualstack" ] @@ -1874,6 +2030,9 @@ "variants" : [ { "hostname" : "athena-fips.us-west-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] }, { "hostname" : "athena.us-west-1.api.aws", "tags" : [ "dualstack" ] @@ -1883,6 +2042,9 @@ "variants" : [ { "hostname" : "athena-fips.us-west-2.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] }, { "hostname" : "athena.us-west-2.api.aws", "tags" : [ "dualstack" ] @@ -1917,13 +2079,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -1973,16 +2139,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -2038,13 +2209,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2076,6 +2251,7 @@ "deprecated" : true, "hostname" : "fips.batch.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -2138,8 +2314,27 @@ }, "cases" : { "endpoints" : { - "us-east-1" : { }, - "us-west-2" : { } + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-us-east-1" : { + "deprecated" : true + }, + "fips-us-west-2" : { + "deprecated" : true + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } } }, "cassandra" : { @@ -2221,6 +2416,21 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-global" }, + "cleanrooms" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, "cloud9" : { "endpoints" : { "af-south-1" : { }, @@ -2254,9 +2464,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "cloudcontrolapi-fips.ca-central-1.amazonaws.com", @@ -2264,8 +2476,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2354,13 +2568,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2457,6 +2675,7 @@ "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, "eu-west-1" : { }, @@ -2493,13 +2712,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2560,6 +2783,32 @@ } } }, + "cloudtrail-data" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "codeartifact" : { "endpoints" : { "ap-northeast-1" : { }, @@ -2585,16 +2834,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -2651,6 +2905,15 @@ } } }, + "codecatalyst" : { + "endpoints" : { + "aws-global" : { + "hostname" : "codecatalyst.global.api.aws" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, "codecommit" : { "endpoints" : { "af-south-1" : { }, @@ -2659,8 +2922,10 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "codecommit-fips.ca-central-1.amazonaws.com", @@ -2687,6 +2952,7 @@ "deprecated" : true, "hostname" : "codecommit-fips.ca-central-1.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -2751,13 +3017,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2834,6 +3104,7 @@ }, "codepipeline" : { "endpoints" : { + "af-south-1" : { }, "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, @@ -2847,6 +3118,7 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, "eu-west-1" : { }, @@ -2887,6 +3159,7 @@ "deprecated" : true, "hostname" : "codepipeline-fips.us-west-2.amazonaws.com" }, + "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { @@ -2941,6 +3214,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2983,6 +3257,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3000,6 +3275,13 @@ "deprecated" : true, "hostname" : "cognito-identity-fips.us-east-2.amazonaws.com" }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com" + }, "fips-us-west-2" : { "credentialScope" : { "region" : "us-west-2" @@ -3021,7 +3303,12 @@ "tags" : [ "fips" ] } ] }, - "us-west-1" : { }, + "us-west-1" : { + "variants" : [ { + "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "us-west-2" : { "variants" : [ { "hostname" : "cognito-identity-fips.us-west-2.amazonaws.com", @@ -3040,6 +3327,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3354,13 +3642,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3431,13 +3723,38 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "connect-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "connect-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "connect-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "connect-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "connect-campaigns" : { "endpoints" : { "ap-southeast-2" : { }, + "ca-central-1" : { }, "eu-west-2" : { }, "fips-us-east-1" : { "credentialScope" : { @@ -3481,11 +3798,15 @@ }, "controltower" : { "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-northeast-3" : { }, "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "controltower-fips.ca-central-1.amazonaws.com", @@ -3501,9 +3822,11 @@ }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { @@ -3531,6 +3854,19 @@ "deprecated" : true, "hostname" : "controltower-fips.us-east-2.amazonaws.com" }, + "us-west-1" : { + "variants" : [ { + "hostname" : "controltower-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "controltower-fips.us-west-1.amazonaws.com" + }, "us-west-2" : { "variants" : [ { "hostname" : "controltower-fips.us-west-2.amazonaws.com", @@ -3926,9 +4262,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "datasync-fips.ca-central-1.amazonaws.com", @@ -3936,8 +4274,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3976,6 +4316,7 @@ "deprecated" : true, "hostname" : "datasync-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -4036,12 +4377,24 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "devops-guru-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-north-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.ca-central-1.amazonaws.com" + }, "fips-us-east-1" : { "credentialScope" : { "region" : "us-east-1" @@ -4056,6 +4409,13 @@ "deprecated" : true, "hostname" : "devops-guru-fips.us-east-2.amazonaws.com" }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.us-west-1.amazonaws.com" + }, "fips-us-west-2" : { "credentialScope" : { "region" : "us-west-2" @@ -4076,7 +4436,12 @@ "tags" : [ "fips" ] } ] }, - "us-west-1" : { }, + "us-west-1" : { + "variants" : [ { + "hostname" : "devops-guru-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "us-west-2" : { "variants" : [ { "hostname" : "devops-guru-fips.us-west-2.amazonaws.com", @@ -4093,13 +4458,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4182,13 +4551,17 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -4205,9 +4578,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "dms" : { "credentialScope" : { @@ -4227,8 +4602,10 @@ "hostname" : "dms-fips.us-west-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4411,9 +4788,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ds-fips.ca-central-1.amazonaws.com", @@ -4421,8 +4800,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4461,6 +4842,7 @@ "deprecated" : true, "hostname" : "ds-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -4500,9 +4882,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "dynamodb-fips.ca-central-1.amazonaws.com", @@ -4517,8 +4901,10 @@ "hostname" : "dynamodb-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4594,9 +4980,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ebs-fips.ca-central-1.amazonaws.com", @@ -4604,8 +4992,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4644,6 +5034,7 @@ "deprecated" : true, "hostname" : "ebs-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -4688,9 +5079,11 @@ "tags" : [ "dualstack" ] } ] }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ec2-fips.ca-central-1.amazonaws.com", @@ -4698,8 +5091,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { "variants" : [ { "hostname" : "ec2.eu-west-1.api.aws", @@ -4794,13 +5189,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4886,13 +5285,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4961,13 +5364,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -5147,6 +5554,12 @@ "tags" : [ "fips" ] } ] }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ap-southeast-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.ap-southeast-1.amazonaws.com", @@ -5165,6 +5578,12 @@ "tags" : [ "fips" ] } ] }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ca-central-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.ca-central-1.amazonaws.com", @@ -5177,6 +5596,12 @@ "tags" : [ "fips" ] } ] }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-north-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.eu-north-1.amazonaws.com", @@ -5189,6 +5614,12 @@ "tags" : [ "fips" ] } ] }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-west-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.eu-west-1.amazonaws.com", @@ -5249,6 +5680,13 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.ap-south-1.amazonaws.com" }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-south-2.amazonaws.com" + }, "fips-ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -5270,6 +5708,13 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.ap-southeast-3.amazonaws.com" }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-southeast-4.amazonaws.com" + }, "fips-ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -5284,6 +5729,13 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.eu-central-1.amazonaws.com" }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-central-2.amazonaws.com" + }, "fips-eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -5298,6 +5750,13 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.eu-south-1.amazonaws.com" }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-south-2.amazonaws.com" + }, "fips-eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -5423,13 +5882,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -5502,9 +5965,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "elasticmapreduce-fips.ca-central-1.amazonaws.com", @@ -5514,8 +5979,10 @@ "eu-central-1" : { "sslCommonName" : "{service}.{region}.{dnsSuffix}" }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -5605,6 +6072,7 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, @@ -5646,6 +6114,7 @@ }, "emr-containers" : { "endpoints" : { + "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, "ap-south-1" : { }, @@ -5697,6 +6166,7 @@ "deprecated" : true, "hostname" : "emr-containers-fips.us-west-2.amazonaws.com" }, + "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { @@ -5726,6 +6196,7 @@ }, "emr-serverless" : { "endpoints" : { + "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, "ap-south-1" : { }, @@ -5777,6 +6248,7 @@ "deprecated" : true, "hostname" : "emr-serverless-fips.us-west-2.amazonaws.com" }, + "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { @@ -5822,13 +6294,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -5904,13 +6380,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -6028,13 +6508,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -6131,6 +6615,7 @@ "tags" : [ "fips" ] } ] }, + "ap-south-2" : { }, "ap-southeast-1" : { "variants" : [ { "hostname" : "fms-fips.ap-southeast-1.amazonaws.com", @@ -6143,6 +6628,8 @@ "tags" : [ "fips" ] } ] }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "fms-fips.ca-central-1.amazonaws.com", @@ -6155,6 +6642,7 @@ "tags" : [ "fips" ] } ] }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { "variants" : [ { @@ -6162,6 +6650,7 @@ "tags" : [ "fips" ] } ] }, + "eu-south-2" : { }, "eu-west-1" : { "variants" : [ { "hostname" : "fms-fips.eu-west-1.amazonaws.com", @@ -6313,6 +6802,7 @@ "deprecated" : true, "hostname" : "fms-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { "variants" : [ { "hostname" : "fms-fips.me-south-1.amazonaws.com", @@ -6469,9 +6959,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "fsx-fips.ca-central-1.amazonaws.com", @@ -6479,8 +6971,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -6554,6 +7048,7 @@ "deprecated" : true, "hostname" : "fsx-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "prod-ca-central-1" : { "credentialScope" : { @@ -6659,17 +7154,22 @@ }, "gamesparks" : { "endpoints" : { + "ap-northeast-1" : { }, "us-east-1" : { } } }, "geo" : { "endpoints" : { "ap-northeast-1" : { }, + "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, "eu-west-1" : { }, + "eu-west-2" : { }, + "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, "us-west-2" : { } @@ -6777,8 +7277,10 @@ "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -6810,6 +7312,7 @@ "deprecated" : true, "hostname" : "glue-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -6912,13 +7415,61 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "greengrass-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-east-2" : { }, - "us-west-2" : { } + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } }, "isRegionalized" : true }, @@ -6985,13 +7536,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -7091,6 +7646,7 @@ "protocols" : [ "https" ] }, "endpoints" : { + "ap-south-1" : { }, "us-east-1" : { }, "us-east-2" : { }, "us-west-2" : { } @@ -7161,16 +7717,22 @@ }, "identitystore" : { "endpoints" : { + "af-south-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-northeast-3" : { }, "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, "us-west-2" : { } @@ -7339,12 +7901,79 @@ "us-west-2" : { } } }, - "iot" : { + "internetmonitor" : { "defaults" : { - "credentialScope" : { - "service" : "execute-api" - } + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] }, + "endpoints" : { + "af-south-1" : { + "hostname" : "internetmonitor.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "internetmonitor.ap-east-1.api.aws" + }, + "ap-northeast-1" : { + "hostname" : "internetmonitor.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "internetmonitor.ap-northeast-2.api.aws" + }, + "ap-south-1" : { + "hostname" : "internetmonitor.ap-south-1.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "internetmonitor.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "internetmonitor.ap-southeast-2.api.aws" + }, + "ca-central-1" : { + "hostname" : "internetmonitor.ca-central-1.api.aws" + }, + "eu-central-1" : { + "hostname" : "internetmonitor.eu-central-1.api.aws" + }, + "eu-north-1" : { + "hostname" : "internetmonitor.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "internetmonitor.eu-south-1.api.aws" + }, + "eu-west-1" : { + "hostname" : "internetmonitor.eu-west-1.api.aws" + }, + "eu-west-2" : { + "hostname" : "internetmonitor.eu-west-2.api.aws" + }, + "eu-west-3" : { + "hostname" : "internetmonitor.eu-west-3.api.aws" + }, + "me-south-1" : { + "hostname" : "internetmonitor.me-south-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "internetmonitor.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "internetmonitor.us-east-1.api.aws" + }, + "us-east-2" : { + "hostname" : "internetmonitor.us-east-2.api.aws" + }, + "us-west-1" : { + "hostname" : "internetmonitor.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "internetmonitor.us-west-2.api.aws" + } + } + }, + "iot" : { "endpoints" : { "ap-east-1" : { }, "ap-northeast-1" : { }, @@ -7364,37 +7993,22 @@ "eu-west-2" : { }, "eu-west-3" : { }, "fips-ca-central-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.ca-central-1.amazonaws.com" }, "fips-us-east-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-east-1.amazonaws.com" }, "fips-us-east-2" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-east-2.amazonaws.com" }, "fips-us-west-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-west-1.amazonaws.com" }, "fips-us-west-2" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-west-2.amazonaws.com" }, @@ -7629,6 +8243,12 @@ "us-east-1" : { } } }, + "iotroborunner" : { + "endpoints" : { + "eu-central-1" : { }, + "us-east-1" : { } + } + }, "iotsecuredtunneling" : { "defaults" : { "variants" : [ { @@ -7801,8 +8421,32 @@ "ap-southeast-2" : { }, "eu-central-1" : { }, "eu-west-1" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "iotwireless" : { @@ -7861,81 +8505,160 @@ "us-west-2" : { } } }, - "kafka" : { + "ivsrealtime" : { "endpoints" : { - "af-south-1" : { }, - "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, - "ap-northeast-3" : { }, "ap-south-1" : { }, - "ap-southeast-1" : { }, - "ap-southeast-2" : { }, - "ap-southeast-3" : { }, - "ca-central-1" : { }, "eu-central-1" : { }, - "eu-north-1" : { }, - "eu-south-1" : { }, "eu-west-1" : { }, - "eu-west-2" : { }, - "eu-west-3" : { }, - "me-south-1" : { }, - "sa-east-1" : { }, "us-east-1" : { }, - "us-east-2" : { }, - "us-west-1" : { }, "us-west-2" : { } } }, - "kafkaconnect" : { + "kafka" : { "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "kafka-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, - "sa-east-1" : { }, - "us-east-1" : { }, - "us-east-2" : { }, - "us-west-1" : { }, - "us-west-2" : { } - } - }, - "kendra" : { - "endpoints" : { - "ap-southeast-1" : { }, - "ap-southeast-2" : { }, - "ca-central-1" : { }, - "eu-west-1" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.ca-central-1.amazonaws.com" + }, "fips-us-east-1" : { "credentialScope" : { "region" : "us-east-1" }, "deprecated" : true, - "hostname" : "kendra-fips.us-east-1.amazonaws.com" + "hostname" : "kafka-fips.us-east-1.amazonaws.com" }, "fips-us-east-2" : { "credentialScope" : { "region" : "us-east-2" }, "deprecated" : true, - "hostname" : "kendra-fips.us-east-2.amazonaws.com" + "hostname" : "kafka-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-west-1.amazonaws.com" }, "fips-us-west-2" : { "credentialScope" : { "region" : "us-west-2" }, "deprecated" : true, - "hostname" : "kendra-fips.us-west-2.amazonaws.com" + "hostname" : "kafka-fips.us-west-2.amazonaws.com" }, - "us-east-1" : { - "variants" : [ { + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "kafka-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "kafka-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "kafka-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "kafka-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kafkaconnect" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "kendra" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "kendra-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "kendra-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "kendra-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { "hostname" : "kendra-fips.us-east-1.amazonaws.com", "tags" : [ "fips" ] } ] @@ -7954,6 +8677,109 @@ } } }, + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "kendra-ranking.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "kendra-ranking.ap-east-1.api.aws" + }, + "ap-northeast-1" : { + "hostname" : "kendra-ranking.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "kendra-ranking.ap-northeast-2.api.aws" + }, + "ap-northeast-3" : { + "hostname" : "kendra-ranking.ap-northeast-3.api.aws" + }, + "ap-south-1" : { + "hostname" : "kendra-ranking.ap-south-1.api.aws" + }, + "ap-south-2" : { + "hostname" : "kendra-ranking.ap-south-2.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "kendra-ranking.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "kendra-ranking.ap-southeast-2.api.aws" + }, + "ap-southeast-3" : { + "hostname" : "kendra-ranking.ap-southeast-3.api.aws" + }, + "ap-southeast-4" : { + "hostname" : "kendra-ranking.ap-southeast-4.api.aws" + }, + "ca-central-1" : { + "hostname" : "kendra-ranking.ca-central-1.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.ca-central-1.api.aws", + "tags" : [ "fips" ] + } ] + }, + "eu-central-2" : { + "hostname" : "kendra-ranking.eu-central-2.api.aws" + }, + "eu-north-1" : { + "hostname" : "kendra-ranking.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "kendra-ranking.eu-south-1.api.aws" + }, + "eu-south-2" : { + "hostname" : "kendra-ranking.eu-south-2.api.aws" + }, + "eu-west-1" : { + "hostname" : "kendra-ranking.eu-west-1.api.aws" + }, + "eu-west-3" : { + "hostname" : "kendra-ranking.eu-west-3.api.aws" + }, + "me-central-1" : { + "hostname" : "kendra-ranking.me-central-1.api.aws" + }, + "me-south-1" : { + "hostname" : "kendra-ranking.me-south-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "kendra-ranking.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "kendra-ranking.us-east-1.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-east-1.api.aws", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "hostname" : "kendra-ranking.us-east-2.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-east-2.api.aws", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "hostname" : "kendra-ranking.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "kendra-ranking.us-west-2.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-west-2.api.aws", + "tags" : [ "fips" ] + } ] + } + } + }, "kinesis" : { "endpoints" : { "af-south-1" : { }, @@ -7962,13 +8788,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -8037,16 +8867,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -8162,6 +8997,12 @@ "deprecated" : true, "hostname" : "kms-fips.ap-south-1.amazonaws.com" }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "kms-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ap-south-2-fips" : { "credentialScope" : { "region" : "ap-south-2" @@ -8208,6 +9049,19 @@ "deprecated" : true, "hostname" : "kms-fips.ap-southeast-3.amazonaws.com" }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "kms-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-4-fips" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-southeast-4.amazonaws.com" + }, "ca-central-1" : { "variants" : [ { "hostname" : "kms-fips.ca-central-1.amazonaws.com", @@ -8234,6 +9088,12 @@ "deprecated" : true, "hostname" : "kms-fips.eu-central-1.amazonaws.com" }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "kms-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-2-fips" : { "credentialScope" : { "region" : "eu-central-2" @@ -8267,6 +9127,12 @@ "deprecated" : true, "hostname" : "kms-fips.eu-south-1.amazonaws.com" }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "kms-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-south-2-fips" : { "credentialScope" : { "region" : "eu-south-2" @@ -8313,6 +9179,12 @@ "deprecated" : true, "hostname" : "kms-fips.eu-west-3.amazonaws.com" }, + "il-central-1-fips" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "kms-fips.il-central-1.amazonaws.com" + }, "me-central-1" : { "variants" : [ { "hostname" : "kms-fips.me-central-1.amazonaws.com", @@ -8416,10 +9288,13 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -8451,6 +9326,7 @@ "deprecated" : true, "hostname" : "lakeformation-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -8517,6 +9393,12 @@ "tags" : [ "dualstack" ] } ] }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "lambda.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, "ap-southeast-1" : { "variants" : [ { "hostname" : "lambda.ap-southeast-1.api.aws", @@ -8535,6 +9417,12 @@ "tags" : [ "dualstack" ] } ] }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "lambda.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, "ca-central-1" : { "variants" : [ { "hostname" : "lambda.ca-central-1.api.aws", @@ -8547,6 +9435,12 @@ "tags" : [ "dualstack" ] } ] }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "lambda.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, "eu-north-1" : { "variants" : [ { "hostname" : "lambda.eu-north-1.api.aws", @@ -8559,6 +9453,12 @@ "tags" : [ "dualstack" ] } ] }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "lambda.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, "eu-west-1" : { "variants" : [ { "hostname" : "lambda.eu-west-1.api.aws", @@ -8735,6 +9635,85 @@ } } }, + "license-manager-linux-subscriptions" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "license-manager-user-subscriptions" : { "endpoints" : { "af-south-1" : { }, @@ -8834,13 +9813,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -8934,13 +9917,56 @@ }, "m2" : { "endpoints" : { - "ap-southeast-2" : { }, - "ca-central-1" : { }, - "eu-central-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "deprecated" : true + }, + "fips-us-east-1" : { + "deprecated" : true + }, + "fips-us-east-2" : { + "deprecated" : true + }, + "fips-us-west-1" : { + "deprecated" : true + }, + "fips-us-west-2" : { + "deprecated" : true + }, "sa-east-1" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "us-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } } }, "machinelearning" : { @@ -9121,6 +10147,7 @@ }, "mediaconvert" : { "endpoints" : { + "af-south-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, "ap-south-1" : { }, @@ -9291,6 +10318,25 @@ "us-west-2" : { } } }, + "mediapackagev2" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "mediastore" : { "endpoints" : { "ap-northeast-1" : { }, @@ -9347,8 +10393,10 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, "fips" : { "credentialScope" : { "region" : "us-west-1" @@ -9393,13 +10441,48 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "metrics.sagemaker" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -9431,22 +10514,75 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, - "us-east-1" : { }, - "us-east-2" : { }, - "us-west-1" : { }, - "us-west-2" : { } + "us-east-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "mgn-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "mgn-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "migrationhub-orchestrator" : { @@ -9547,13 +10683,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -9622,13 +10762,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -9660,6 +10804,7 @@ "deprecated" : true, "hostname" : "mq-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -9867,6 +11012,7 @@ "deprecated" : true, "hostname" : "network-firewall-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -9917,8 +11063,45 @@ "us-west-2" : { } } }, + "oam" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "oidc" : { "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "oidc.af-south-1.amazonaws.com" + }, "ap-east-1" : { "credentialScope" : { "region" : "ap-east-1" @@ -9961,6 +11144,12 @@ }, "hostname" : "oidc.ap-southeast-2.amazonaws.com" }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "oidc.ap-southeast-3.amazonaws.com" + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -10027,6 +11216,12 @@ }, "hostname" : "oidc.us-east-2.amazonaws.com" }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "oidc.us-west-1.amazonaws.com" + }, "us-west-2" : { "credentialScope" : { "region" : "us-west-2" @@ -10035,6 +11230,68 @@ } } }, + "omics" : { + "endpoints" : { + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "omics.ap-southeast-1.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "omics.eu-central-1.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "omics.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "omics.eu-west-2.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "omics-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "omics-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "omics.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "omics-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "omics.us-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "omics-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "opsworks" : { "endpoints" : { "ap-northeast-1" : { }, @@ -10090,6 +11347,20 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-global" }, + "osis" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "outposts" : { "endpoints" : { "af-south-1" : { }, @@ -10237,13 +11508,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -10341,12 +11616,40 @@ } } }, - "polly" : { + "pipes" : { "endpoints" : { "af-south-1" : { }, "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "polly" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, @@ -10414,6 +11717,12 @@ }, "portal.sso" : { "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "portal.sso.af-south-1.amazonaws.com" + }, "ap-east-1" : { "credentialScope" : { "region" : "ap-east-1" @@ -10456,6 +11765,12 @@ }, "hostname" : "portal.sso.ap-southeast-2.amazonaws.com" }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "portal.sso.ap-southeast-3.amazonaws.com" + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -10522,6 +11837,12 @@ }, "hostname" : "portal.sso.us-east-2.amazonaws.com" }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "portal.sso.us-west-1.amazonaws.com" + }, "us-west-2" : { "credentialScope" : { "region" : "us-west-2" @@ -10537,11 +11858,47 @@ "ap-northeast-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "profile-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "profile-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "profile-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "profile-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "profile-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "profile-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "projects.iot1click" : { @@ -10558,7 +11915,13 @@ "proton" : { "endpoints" : { "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, "eu-west-1" : { }, + "eu-west-2" : { }, "us-east-1" : { }, "us-east-2" : { }, "us-west-2" : { } @@ -10634,11 +11997,12 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "api" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-north-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, @@ -10653,9 +12017,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ram-fips.ca-central-1.amazonaws.com", @@ -10663,8 +12029,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -10740,9 +12108,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "rbin-fips.ca-central-1.amazonaws.com", @@ -10750,8 +12120,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -10827,9 +12199,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "rds-fips.ca-central-1.amazonaws.com", @@ -10844,8 +12218,10 @@ "hostname" : "rds-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -11066,9 +12442,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "redshift-fips.ca-central-1.amazonaws.com", @@ -11076,8 +12454,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -11149,14 +12529,18 @@ "endpoints" : { "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, "us-east-1" : { }, "us-east-2" : { }, + "us-west-1" : { }, "us-west-2" : { } } }, @@ -11348,6 +12732,7 @@ }, "resource-explorer-2" : { "defaults" : { + "dnsSuffix" : "api.aws", "variants" : [ { "dnsSuffix" : "api.aws", "hostname" : "{service}-fips.{region}.{dnsSuffix}", @@ -11355,12 +12740,6 @@ } ] }, "endpoints" : { - "af-south-1" : { - "hostname" : "resource-explorer-2.af-south-1.api.aws" - }, - "ap-east-1" : { - "hostname" : "resource-explorer-2.ap-east-1.api.aws" - }, "ap-northeast-1" : { "hostname" : "resource-explorer-2.ap-northeast-1.api.aws" }, @@ -11373,18 +12752,27 @@ "ap-south-1" : { "hostname" : "resource-explorer-2.ap-south-1.api.aws" }, + "ap-south-2" : { + "hostname" : "resource-explorer-2.ap-south-2.api.aws" + }, "ap-southeast-1" : { "hostname" : "resource-explorer-2.ap-southeast-1.api.aws" }, "ap-southeast-2" : { "hostname" : "resource-explorer-2.ap-southeast-2.api.aws" }, + "ap-southeast-4" : { + "hostname" : "resource-explorer-2.ap-southeast-4.api.aws" + }, "ca-central-1" : { "hostname" : "resource-explorer-2.ca-central-1.api.aws" }, "eu-central-1" : { "hostname" : "resource-explorer-2.eu-central-1.api.aws" }, + "eu-central-2" : { + "hostname" : "resource-explorer-2.eu-central-2.api.aws" + }, "eu-north-1" : { "hostname" : "resource-explorer-2.eu-north-1.api.aws" }, @@ -11422,13 +12810,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -11460,6 +12852,7 @@ "deprecated" : true, "hostname" : "resource-groups-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -11574,16 +12967,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -11680,16 +13078,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -11799,6 +13202,12 @@ "tags" : [ "dualstack" ] } ] }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "ap-southeast-1" : { "hostname" : "s3.ap-southeast-1.amazonaws.com", "signatureVersions" : [ "s3", "s3v4" ], @@ -11821,6 +13230,12 @@ "tags" : [ "dualstack" ] } ] }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-southeast-4.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "aws-global" : { "credentialScope" : { "region" : "us-east-1" @@ -11846,6 +13261,12 @@ "tags" : [ "dualstack" ] } ] }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-central-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "eu-north-1" : { "variants" : [ { "hostname" : "s3.dualstack.eu-north-1.amazonaws.com", @@ -11858,6 +13279,12 @@ "tags" : [ "dualstack" ] } ] }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "eu-west-1" : { "hostname" : "s3.eu-west-1.amazonaws.com", "signatureVersions" : [ "s3", "s3v4" ], @@ -12333,6 +13760,11 @@ } } }, + "sagemaker-geospatial" : { + "endpoints" : { + "us-west-2" : { } + } + }, "savingsplans" : { "endpoints" : { "aws-global" : { @@ -12345,6 +13777,37 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-global" }, + "scheduler" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "schemas" : { "endpoints" : { "ap-east-1" : { }, @@ -12392,9 +13855,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "secretsmanager-fips.ca-central-1.amazonaws.com", @@ -12409,8 +13874,10 @@ "hostname" : "secretsmanager-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -12479,13 +13946,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -12517,6 +13988,7 @@ "deprecated" : true, "hostname" : "securityhub-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -12545,6 +14017,23 @@ } } }, + "securitylake" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "serverlessrepo" : { "defaults" : { "protocols" : [ "https" ] @@ -12614,16 +14103,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -12738,6 +14232,7 @@ "deprecated" : true, "hostname" : "servicecatalog-appregistry-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -12768,101 +14263,230 @@ }, "servicediscovery" : { "endpoints" : { - "af-south-1" : { }, - "ap-east-1" : { }, - "ap-northeast-1" : { }, - "ap-northeast-2" : { }, - "ap-northeast-3" : { }, - "ap-south-1" : { }, - "ap-southeast-1" : { }, - "ap-southeast-2" : { }, - "ap-southeast-3" : { }, - "ca-central-1" : { + "af-south-1" : { "variants" : [ { - "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com", - "tags" : [ "fips" ] + "hostname" : "servicediscovery.af-south-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, - "ca-central-1-fips" : { - "credentialScope" : { - "region" : "ca-central-1" - }, - "deprecated" : true, - "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com" - }, - "eu-central-1" : { }, - "eu-north-1" : { }, - "eu-south-1" : { }, - "eu-west-1" : { }, - "eu-west-2" : { }, - "eu-west-3" : { }, - "me-central-1" : { }, - "me-south-1" : { }, - "sa-east-1" : { }, - "servicediscovery" : { - "credentialScope" : { - "region" : "ca-central-1" - }, - "deprecated" : true, + "ap-east-1" : { "variants" : [ { - "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com", - "tags" : [ "fips" ] + "hostname" : "servicediscovery.ap-east-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, - "servicediscovery-fips" : { - "credentialScope" : { - "region" : "ca-central-1" - }, - "deprecated" : true, - "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com" - }, - "us-east-1" : { + "ap-northeast-1" : { "variants" : [ { - "hostname" : "servicediscovery-fips.us-east-1.amazonaws.com", - "tags" : [ "fips" ] + "hostname" : "servicediscovery.ap-northeast-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, - "us-east-1-fips" : { - "credentialScope" : { - "region" : "us-east-1" - }, - "deprecated" : true, - "hostname" : "servicediscovery-fips.us-east-1.amazonaws.com" - }, - "us-east-2" : { + "ap-northeast-2" : { "variants" : [ { - "hostname" : "servicediscovery-fips.us-east-2.amazonaws.com", - "tags" : [ "fips" ] + "hostname" : "servicediscovery.ap-northeast-2.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, - "us-east-2-fips" : { - "credentialScope" : { - "region" : "us-east-2" - }, - "deprecated" : true, - "hostname" : "servicediscovery-fips.us-east-2.amazonaws.com" - }, - "us-west-1" : { + "ap-northeast-3" : { "variants" : [ { - "hostname" : "servicediscovery-fips.us-west-1.amazonaws.com", - "tags" : [ "fips" ] + "hostname" : "servicediscovery.ap-northeast-3.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, - "us-west-1-fips" : { - "credentialScope" : { - "region" : "us-west-1" - }, - "deprecated" : true, - "hostname" : "servicediscovery-fips.us-west-1.amazonaws.com" + "ap-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] }, - "us-west-2" : { + "ap-south-2" : { "variants" : [ { - "hostname" : "servicediscovery-fips.us-west-2.amazonaws.com", - "tags" : [ "fips" ] + "hostname" : "servicediscovery.ap-south-2.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, - "us-west-2-fips" : { - "credentialScope" : { + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-4.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.ca-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-central-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-north-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery.me-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.me-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "servicediscovery.sa-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "servicediscovery" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "variants" : [ { + "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "servicediscovery-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-east-1.amazonaws.com" + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-east-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-east-2-fips" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-east-2.amazonaws.com" + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "servicediscovery-fips.us-west-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "servicediscovery-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { "region" : "us-west-2" }, "deprecated" : true, @@ -12881,16 +14505,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -12977,6 +14606,90 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-global" }, + "signer" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "signer-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "signer-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "signer-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "signer-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "simspaceweaver" : { + "endpoints" : { + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, "sms" : { "endpoints" : { "af-south-1" : { }, @@ -13055,12 +14768,48 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "snowball" : { @@ -13248,6 +14997,7 @@ "deprecated" : true, "hostname" : "snowball-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "sa-east-1" : { "variants" : [ { "hostname" : "snowball-fips.sa-east-1.amazonaws.com", @@ -13291,13 +15041,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13370,13 +15124,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13439,6 +15197,117 @@ } }, "ssm" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ssm-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ssm-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ssm-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ssm-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ssm-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ssm-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ssm-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ssm-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ssm-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ssm-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "ssm-incidents" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "ssm-sap" : { "endpoints" : { "af-south-1" : { }, "ap-east-1" : { }, @@ -13451,7 +15320,7 @@ "ap-southeast-3" : { }, "ca-central-1" : { "variants" : [ { - "hostname" : "ssm-fips.ca-central-1.amazonaws.com", + "hostname" : "ssm-sap-fips.ca-central-1.amazonaws.com", "tags" : [ "fips" ] } ] }, @@ -13466,87 +15335,67 @@ "region" : "ca-central-1" }, "deprecated" : true, - "hostname" : "ssm-fips.ca-central-1.amazonaws.com" + "hostname" : "ssm-sap-fips.ca-central-1.amazonaws.com" }, "fips-us-east-1" : { "credentialScope" : { "region" : "us-east-1" }, "deprecated" : true, - "hostname" : "ssm-fips.us-east-1.amazonaws.com" + "hostname" : "ssm-sap-fips.us-east-1.amazonaws.com" }, "fips-us-east-2" : { "credentialScope" : { "region" : "us-east-2" }, "deprecated" : true, - "hostname" : "ssm-fips.us-east-2.amazonaws.com" + "hostname" : "ssm-sap-fips.us-east-2.amazonaws.com" }, "fips-us-west-1" : { "credentialScope" : { "region" : "us-west-1" }, "deprecated" : true, - "hostname" : "ssm-fips.us-west-1.amazonaws.com" + "hostname" : "ssm-sap-fips.us-west-1.amazonaws.com" }, "fips-us-west-2" : { "credentialScope" : { "region" : "us-west-2" }, "deprecated" : true, - "hostname" : "ssm-fips.us-west-2.amazonaws.com" + "hostname" : "ssm-sap-fips.us-west-2.amazonaws.com" }, - "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { - "hostname" : "ssm-fips.us-east-1.amazonaws.com", + "hostname" : "ssm-sap-fips.us-east-1.amazonaws.com", "tags" : [ "fips" ] } ] }, "us-east-2" : { "variants" : [ { - "hostname" : "ssm-fips.us-east-2.amazonaws.com", + "hostname" : "ssm-sap-fips.us-east-2.amazonaws.com", "tags" : [ "fips" ] } ] }, "us-west-1" : { "variants" : [ { - "hostname" : "ssm-fips.us-west-1.amazonaws.com", + "hostname" : "ssm-sap-fips.us-west-1.amazonaws.com", "tags" : [ "fips" ] } ] }, "us-west-2" : { "variants" : [ { - "hostname" : "ssm-fips.us-west-2.amazonaws.com", + "hostname" : "ssm-sap-fips.us-west-2.amazonaws.com", "tags" : [ "fips" ] } ] } } }, - "ssm-incidents" : { - "endpoints" : { - "ap-northeast-1" : { }, - "ap-northeast-2" : { }, - "ap-south-1" : { }, - "ap-southeast-1" : { }, - "ap-southeast-2" : { }, - "ca-central-1" : { }, - "eu-central-1" : { }, - "eu-north-1" : { }, - "eu-west-1" : { }, - "eu-west-2" : { }, - "eu-west-3" : { }, - "sa-east-1" : { }, - "us-east-1" : { }, - "us-east-2" : { }, - "us-west-1" : { }, - "us-west-2" : { } - } - }, "sso" : { "endpoints" : { + "af-south-1" : { }, "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, @@ -13554,6 +15403,7 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, @@ -13565,6 +15415,7 @@ "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, + "us-west-1" : { }, "us-west-2" : { } } }, @@ -13576,13 +15427,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13651,9 +15506,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "storagegateway-fips.ca-central-1.amazonaws.com", @@ -13668,8 +15525,10 @@ "hostname" : "storagegateway-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13680,6 +15539,7 @@ "deprecated" : true, "hostname" : "storagegateway-fips.ca-central-1.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -13750,13 +15610,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13784,9 +15648,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "aws-global" : { "credentialScope" : { "region" : "us-east-1" @@ -13795,8 +15661,10 @@ }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13884,13 +15752,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13959,13 +15831,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -14034,13 +15910,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -14222,8 +16102,11 @@ }, "transcribestreaming" : { "endpoints" : { + "af-south-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ca-central-1" : { }, "eu-central-1" : { }, @@ -14311,6 +16194,7 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, @@ -14321,8 +16205,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -14361,6 +16247,7 @@ "deprecated" : true, "hostname" : "transfer-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -14448,31 +16335,145 @@ } } }, + "verifiedpermissions" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, + "voice-chime" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "voice-chime-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "voice-chime-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "voice-chime-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.us-west-2.amazonaws.com" + } + } + }, "voiceid" : { "endpoints" : { "ap-northeast-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "voiceid-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.ca-central-1.amazonaws.com" + }, "fips-us-east-1" : { - "deprecated" : true + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.us-east-1.amazonaws.com" }, "fips-us-west-2" : { - "deprecated" : true + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.us-west-2.amazonaws.com" }, "us-east-1" : { "variants" : [ { + "hostname" : "voiceid-fips.us-east-1.amazonaws.com", "tags" : [ "fips" ] } ] }, "us-west-2" : { "variants" : [ { + "hostname" : "voiceid-fips.us-west-2.amazonaws.com", "tags" : [ "fips" ] } ] } } }, + "vpc-lattice" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, "waf" : { "endpoints" : { "aws" : { @@ -14575,6 +16576,16 @@ "tags" : [ "fips" ] } ] }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "waf-regional.ap-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -14605,6 +16616,16 @@ "tags" : [ "fips" ] } ] }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "waf-regional.ap-southeast-4.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -14625,6 +16646,16 @@ "tags" : [ "fips" ] } ] }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "waf-regional.eu-central-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -14645,6 +16676,16 @@ "tags" : [ "fips" ] } ] }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "waf-regional.eu-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -14717,6 +16758,13 @@ "deprecated" : true, "hostname" : "waf-regional-fips.ap-south-1.amazonaws.com" }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-south-2.amazonaws.com" + }, "fips-ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -14738,6 +16786,13 @@ "deprecated" : true, "hostname" : "waf-regional-fips.ap-southeast-3.amazonaws.com" }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-southeast-4.amazonaws.com" + }, "fips-ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -14752,6 +16807,13 @@ "deprecated" : true, "hostname" : "waf-regional-fips.eu-central-1.amazonaws.com" }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-central-2.amazonaws.com" + }, "fips-eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -14766,6 +16828,13 @@ "deprecated" : true, "hostname" : "waf-regional-fips.eu-south-1.amazonaws.com" }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-south-2.amazonaws.com" + }, "fips-eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -14787,6 +16856,19 @@ "deprecated" : true, "hostname" : "waf-regional-fips.eu-west-3.amazonaws.com" }, + "fips-il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "waf-regional-fips.il-central-1.amazonaws.com" + }, + "fips-me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.me-central-1.amazonaws.com" + }, "fips-me-south-1" : { "credentialScope" : { "region" : "me-south-1" @@ -14829,6 +16911,16 @@ "deprecated" : true, "hostname" : "waf-regional-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "waf-regional.me-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "me-south-1" : { "credentialScope" : { "region" : "me-south-1" @@ -14953,6 +17045,16 @@ "tags" : [ "fips" ] } ] }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "wafv2.ap-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -14983,6 +17085,16 @@ "tags" : [ "fips" ] } ] }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "wafv2.ap-southeast-4.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -15003,6 +17115,16 @@ "tags" : [ "fips" ] } ] }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "wafv2.eu-central-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -15023,6 +17145,16 @@ "tags" : [ "fips" ] } ] }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "wafv2.eu-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -15095,6 +17227,13 @@ "deprecated" : true, "hostname" : "wafv2-fips.ap-south-1.amazonaws.com" }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-south-2.amazonaws.com" + }, "fips-ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -15116,6 +17255,13 @@ "deprecated" : true, "hostname" : "wafv2-fips.ap-southeast-3.amazonaws.com" }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-southeast-4.amazonaws.com" + }, "fips-ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -15130,6 +17276,13 @@ "deprecated" : true, "hostname" : "wafv2-fips.eu-central-1.amazonaws.com" }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-central-2.amazonaws.com" + }, "fips-eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -15144,6 +17297,13 @@ "deprecated" : true, "hostname" : "wafv2-fips.eu-south-1.amazonaws.com" }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-south-2.amazonaws.com" + }, "fips-eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -15158,12 +17318,25 @@ "deprecated" : true, "hostname" : "wafv2-fips.eu-west-2.amazonaws.com" }, - "fips-eu-west-3" : { + "fips-eu-west-3" : { + "credentialScope" : { + "region" : "eu-west-3" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-west-3.amazonaws.com" + }, + "fips-il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "wafv2-fips.il-central-1.amazonaws.com" + }, + "fips-me-central-1" : { "credentialScope" : { - "region" : "eu-west-3" + "region" : "me-central-1" }, "deprecated" : true, - "hostname" : "wafv2-fips.eu-west-3.amazonaws.com" + "hostname" : "wafv2-fips.me-central-1.amazonaws.com" }, "fips-me-south-1" : { "credentialScope" : { @@ -15207,6 +17380,16 @@ "deprecated" : true, "hostname" : "wafv2-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "wafv2.me-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "me-south-1" : { "credentialScope" : { "region" : "me-south-1" @@ -15428,13 +17611,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -15552,6 +17739,12 @@ "cn-northwest-1" : { } } }, + "airflow" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "api.ecr" : { "endpoints" : { "cn-north-1" : { @@ -15637,8 +17830,18 @@ }, "athena" : { "endpoints" : { - "cn-north-1" : { }, - "cn-northwest-1" : { } + "cn-north-1" : { + "variants" : [ { + "hostname" : "athena.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "athena.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } } }, "autoscaling" : { @@ -15796,7 +17999,10 @@ "protocols" : [ "https" ] }, "endpoints" : { - "cn-north-1" : { }, + "cn-north-1" : { + "hostname" : "data.ats.iot.cn-north-1.amazonaws.com.cn", + "protocols" : [ "https" ] + }, "cn-northwest-1" : { } } }, @@ -15824,6 +18030,12 @@ "cn-northwest-1" : { } } }, + "datasync" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "dax" : { "endpoints" : { "cn-north-1" : { }, @@ -15969,6 +18181,12 @@ "cn-northwest-1" : { } } }, + "emr-serverless" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "es" : { "endpoints" : { "cn-north-1" : { }, @@ -16080,12 +18298,25 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-cn-global" }, - "iot" : { + "internetmonitor" : { "defaults" : { - "credentialScope" : { - "service" : "execute-api" - } + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "internetmonitor.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "internetmonitor.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, + "iot" : { "endpoints" : { "cn-north-1" : { }, "cn-northwest-1" : { } @@ -16128,6 +18359,24 @@ "cn-northwest-1" : { } } }, + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "kendra-ranking.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "kendra-ranking.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, "kinesis" : { "endpoints" : { "cn-north-1" : { }, @@ -16140,6 +18389,11 @@ "cn-northwest-1" : { } } }, + "kinesisvideo" : { + "endpoints" : { + "cn-north-1" : { } + } + }, "kms" : { "endpoints" : { "cn-north-1" : { }, @@ -16174,6 +18428,12 @@ "cn-northwest-1" : { } } }, + "license-manager-linux-subscriptions" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "logs" : { "endpoints" : { "cn-north-1" : { }, @@ -16196,6 +18456,12 @@ "cn-northwest-1" : { } } }, + "metrics.sagemaker" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "monitoring" : { "defaults" : { "protocols" : [ "http", "https" ] @@ -16227,6 +18493,12 @@ } } }, + "oam" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "organizations" : { "endpoints" : { "aws-cn-global" : { @@ -16281,6 +18553,7 @@ }, "resource-explorer-2" : { "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", "variants" : [ { "dnsSuffix" : "api.amazonwebservices.com.cn", "hostname" : "{service}-fips.{region}.{dnsSuffix}", @@ -16302,6 +18575,12 @@ "cn-northwest-1" : { } } }, + "rolesanywhere" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "route53" : { "endpoints" : { "aws-cn-global" : { @@ -16421,6 +18700,31 @@ } }, "servicediscovery" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "servicediscovery.cn-north-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "servicediscovery.cn-northwest-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "servicequotas" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "signer" : { "endpoints" : { "cn-north-1" : { }, "cn-northwest-1" : { } @@ -16703,12 +19007,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com" } } @@ -16891,6 +19217,7 @@ } ] }, "endpoints" : { + "us-gov-east-1" : { }, "us-gov-west-1" : { "variants" : [ { "hostname" : "api-fips.sagemaker.us-gov-west-1.amazonaws.com", @@ -17007,9 +19334,29 @@ }, "endpoints" : { "us-gov-east-1" : { + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "deprecated" : true, + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", "protocols" : [ "http", "https" ] }, "us-gov-west-1" : { + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "deprecated" : true, + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", "protocols" : [ "http", "https" ] } } @@ -17045,6 +19392,19 @@ "deprecated" : true, "hostname" : "appstream2-fips.us-gov-west-1.amazonaws.com" }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "appstream2-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "appstream2-fips.us-gov-east-1.amazonaws.com" + }, "us-gov-west-1" : { "variants" : [ { "hostname" : "appstream2-fips.us-gov-west-1.amazonaws.com", @@ -17080,12 +19440,24 @@ "variants" : [ { "hostname" : "athena-fips.us-gov-east-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] } ] }, "us-gov-west-1" : { "variants" : [ { "hostname" : "athena-fips.us-gov-west-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] } ] } } @@ -17173,12 +19545,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "cassandra.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "cassandra.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "cassandra.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "cassandra.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "cassandra.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "cassandra.us-gov-west-1.amazonaws.com" } } @@ -17215,7 +19609,19 @@ }, "clouddirectory" : { "endpoints" : { - "us-gov-west-1" : { } + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "clouddirectory.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "clouddirectory.us-gov-west-1.amazonaws.com" + } } }, "cloudformation" : { @@ -17224,12 +19630,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "cloudformation.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "cloudformation.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "cloudformation.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "cloudformation.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "cloudformation.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "cloudformation.us-gov-west-1.amazonaws.com" } } @@ -17392,6 +19820,7 @@ "deprecated" : true, "hostname" : "codepipeline-fips.us-gov-west-1.amazonaws.com" }, + "us-gov-east-1" : { }, "us-gov-west-1" : { "variants" : [ { "hostname" : "codepipeline-fips.us-gov-west-1.amazonaws.com", @@ -17471,6 +19900,22 @@ } } }, + "compute-optimizer" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "compute-optimizer-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "compute-optimizer-fips.us-gov-west-1.amazonaws.com" + } + } + }, "config" : { "defaults" : { "variants" : [ { @@ -17507,11 +19952,23 @@ } } }, - "connect" : { - "endpoints" : { - "us-gov-west-1" : { } - } - }, + "connect" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "connect.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "connect.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "controltower" : { "endpoints" : { "us-gov-east-1" : { }, @@ -17622,7 +20079,19 @@ }, "databrew" : { "endpoints" : { - "us-gov-west-1" : { } + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "databrew.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "databrew.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "datasync" : { @@ -17673,8 +20142,32 @@ }, "dlm" : { "endpoints" : { - "us-gov-east-1" : { }, - "us-gov-west-1" : { } + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "dlm.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "dlm.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "dlm.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "dlm.us-gov-west-1.amazonaws.com" + } } }, "dms" : { @@ -17946,12 +20439,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com" } } @@ -18077,6 +20592,12 @@ } } }, + "emr-containers" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, "es" : { "endpoints" : { "fips" : { @@ -18273,18 +20794,32 @@ }, "glacier" : { "endpoints" : { - "us-gov-east-1" : { + "fips-us-gov-east-1" : { "credentialScope" : { "region" : "us-gov-east-1" }, + "deprecated" : true, "hostname" : "glacier.us-gov-east-1.amazonaws.com" }, - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, - "hostname" : "glacier.us-gov-west-1.amazonaws.com", - "protocols" : [ "http", "https" ] + "deprecated" : true, + "hostname" : "glacier.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "glacier.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "glacier.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, @@ -18340,23 +20875,26 @@ "region" : "us-gov-east-1" }, "deprecated" : true, - "hostname" : "greengrass-fips.us-gov-east-1.amazonaws.com" + "hostname" : "greengrass.us-gov-east-1.amazonaws.com" }, - "us-gov-east-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { - "region" : "us-gov-east-1" + "region" : "us-gov-west-1" }, - "hostname" : "greengrass.us-gov-east-1.amazonaws.com", + "deprecated" : true, + "hostname" : "greengrass.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { "variants" : [ { - "hostname" : "greengrass-fips.us-gov-east-1.amazonaws.com", + "hostname" : "greengrass.us-gov-east-1.amazonaws.com", "tags" : [ "fips" ] } ] }, "us-gov-west-1" : { - "credentialScope" : { - "region" : "us-gov-west-1" - }, - "hostname" : "greengrass.us-gov-west-1.amazonaws.com" + "variants" : [ { + "hostname" : "greengrass.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } }, "isRegionalized" : true @@ -18496,6 +21034,23 @@ } } }, + "ingest.timestream" : { + "endpoints" : { + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "ingest.timestream.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ingest.timestream.us-gov-west-1.amazonaws.com" + } + } + }, "inspector" : { "endpoints" : { "fips-us-gov-east-1" : { @@ -18526,24 +21081,37 @@ } } }, - "iot" : { + "inspector2" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "internetmonitor" : { "defaults" : { - "credentialScope" : { - "service" : "execute-api" - } + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "internetmonitor.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "internetmonitor.us-gov-west-1.api.aws" + } + } + }, + "iot" : { "endpoints" : { "fips-us-gov-east-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-gov-east-1.amazonaws.com" }, "fips-us-gov-west-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-gov-west-1.amazonaws.com" }, @@ -18652,10 +21220,59 @@ } } }, + "iottwinmaker" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "kafka" : { "endpoints" : { - "us-gov-east-1" : { }, - "us-gov-west-1" : { } + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "kafka.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "kafka.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "kafka.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "kafka.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "kafka.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kafka.us-gov-west-1.amazonaws.com" + } } }, "kendra" : { @@ -18675,19 +21292,59 @@ } } }, - "kinesis" : { + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, "endpoints" : { "us-gov-east-1" : { + "hostname" : "kendra-ranking.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "kendra-ranking.us-gov-west-1.api.aws" + } + } + }, + "kinesis" : { + "endpoints" : { + "fips-us-gov-east-1" : { "credentialScope" : { "region" : "us-gov-east-1" }, + "deprecated" : true, "hostname" : "kinesis.us-gov-east-1.amazonaws.com" }, - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "deprecated" : true, "hostname" : "kinesis.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "kinesis.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "kinesis.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "kinesis.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "kinesis.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, @@ -18867,11 +21524,18 @@ }, "mediaconvert" : { "endpoints" : { - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "deprecated" : true, "hostname" : "mediaconvert.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "mediaconvert.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, @@ -18916,6 +21580,42 @@ "us-gov-west-1" : { } } }, + "metrics.sagemaker" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "mgn" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "models.lex" : { "defaults" : { "credentialScope" : { @@ -19107,30 +21807,57 @@ }, "outposts" : { "endpoints" : { - "us-gov-east-1" : { + "fips-us-gov-east-1" : { "credentialScope" : { "region" : "us-gov-east-1" }, + "deprecated" : true, "hostname" : "outposts.us-gov-east-1.amazonaws.com" }, - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "deprecated" : true, "hostname" : "outposts.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "outposts.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "outposts.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, "participant.connect" : { "endpoints" : { - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "deprecated" : true, "hostname" : "participant.connect.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "participant.connect.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, + "pi" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, "pinpoint" : { "defaults" : { "credentialScope" : { @@ -19202,12 +21929,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "ram.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "ram.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "ram.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "ram.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "ram.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "ram.us-gov-west-1.amazonaws.com" } } @@ -19344,6 +22093,7 @@ }, "resource-explorer-2" : { "defaults" : { + "dnsSuffix" : "api.aws", "variants" : [ { "dnsSuffix" : "api.aws", "hostname" : "{service}-fips.{region}.{dnsSuffix}", @@ -19425,8 +22175,26 @@ }, "route53resolver" : { "endpoints" : { - "us-gov-east-1" : { }, - "us-gov-west-1" : { } + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "route53resolver.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "deprecated" : true, + "hostname" : "route53resolver.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "route53resolver.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "deprecated" : true, + "hostname" : "route53resolver.us-gov-west-1.amazonaws.com" + } } }, "runtime.lex" : { @@ -19463,6 +22231,7 @@ } ] }, "endpoints" : { + "us-gov-east-1" : { }, "us-gov-west-1" : { "variants" : [ { "hostname" : "runtime.sagemaker.us-gov-west-1.amazonaws.com", @@ -19683,18 +22452,32 @@ }, "endpoints" : { "us-gov-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { "credentialScope" : { "region" : "us-gov-east-1" }, - "hostname" : "serverlessrepo.us-gov-east-1.amazonaws.com", - "protocols" : [ "https" ] + "deprecated" : true, + "hostname" : "serverlessrepo.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { "credentialScope" : { "region" : "us-gov-west-1" }, - "hostname" : "serverlessrepo.us-gov-west-1.amazonaws.com", - "protocols" : [ "https" ] + "deprecated" : true, + "hostname" : "serverlessrepo.us-gov-west-1.amazonaws.com" } } }, @@ -19763,6 +22546,9 @@ "variants" : [ { "hostname" : "servicediscovery-fips.us-gov-east-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-gov-east-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "us-gov-east-1-fips" : { @@ -19776,6 +22562,9 @@ "variants" : [ { "hostname" : "servicediscovery-fips.us-gov-west-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "us-gov-west-1-fips" : { @@ -19824,6 +22613,12 @@ } } }, + "simspaceweaver" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, "sms" : { "endpoints" : { "fips-us-gov-east-1" : { @@ -19856,7 +22651,19 @@ }, "sms-voice" : { "endpoints" : { - "us-gov-west-1" : { } + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "snowball" : { @@ -19912,7 +22719,7 @@ } ] }, "us-gov-west-1" : { - "protocols" : [ "http", "https" ], + "protocols" : [ "https" ], "variants" : [ { "hostname" : "sns.us-gov-west-1.amazonaws.com", "tags" : [ "fips" ] @@ -20172,12 +22979,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "swf.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "swf.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "swf.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "swf.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "swf.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "swf.us-gov-west-1.amazonaws.com" } } @@ -20425,6 +23254,13 @@ }, "workspaces" : { "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-gov-east-1.amazonaws.com" + }, "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" @@ -20432,6 +23268,12 @@ "deprecated" : true, "hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com" }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "us-gov-west-1" : { "variants" : [ { "hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com", @@ -20542,6 +23384,11 @@ "us-iso-west-1" : { } } }, + "athena" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, "autoscaling" : { "endpoints" : { "us-iso-east-1" : { @@ -20550,6 +23397,12 @@ "us-iso-west-1" : { } } }, + "cloudcontrolapi" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, "cloudformation" : { "endpoints" : { "us-iso-east-1" : { }, @@ -20593,6 +23446,12 @@ "us-iso-west-1" : { } } }, + "dlm" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, "dms" : { "defaults" : { "variants" : [ { @@ -20700,11 +23559,24 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.us-iso-east-1.c2s.ic.gov" }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-iso-west-1.c2s.ic.gov" + }, "us-iso-east-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.us-iso-east-1.c2s.ic.gov", "tags" : [ "fips" ] } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] } } }, @@ -20738,7 +23610,8 @@ }, "firehose" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "glacier" : { @@ -20749,6 +23622,11 @@ "us-iso-west-1" : { } } }, + "glue" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, "health" : { "endpoints" : { "us-iso-east-1" : { } @@ -20817,7 +23695,8 @@ }, "license-manager" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "logs" : { @@ -20836,6 +23715,11 @@ "us-iso-east-1" : { } } }, + "metrics.sagemaker" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, "monitoring" : { "endpoints" : { "us-iso-east-1" : { }, @@ -20853,6 +23737,23 @@ "us-iso-west-1" : { } } }, + "rbin" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, "rds" : { "endpoints" : { "us-iso-east-1" : { }, @@ -20879,7 +23780,8 @@ }, "route53resolver" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "runtime.sagemaker" : { @@ -20901,7 +23803,8 @@ }, "secretsmanager" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "snowball" : { @@ -20979,7 +23882,8 @@ }, "tagging" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "transcribe" : { @@ -21005,7 +23909,8 @@ }, "workspaces" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } } } @@ -21091,6 +23996,11 @@ "us-isob-east-1" : { } } }, + "dlm" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, "dms" : { "defaults" : { "variants" : [ { @@ -21290,6 +24200,11 @@ "us-isob-east-1" : { } } }, + "metrics.sagemaker" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, "monitoring" : { "endpoints" : { "us-isob-east-1" : { } @@ -21300,6 +24215,23 @@ "us-isob-east-1" : { } } }, + "rbin" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + } + } + }, "rds" : { "endpoints" : { "us-isob-east-1" : { } @@ -21341,6 +24273,11 @@ "us-isob-east-1" : { } } }, + "secretsmanager" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, "snowball" : { "endpoints" : { "us-isob-east-1" : { } @@ -21421,6 +24358,40 @@ } } } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "cloud.adc-e.uk", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "dnsSuffix" : "cloud.adc-e.uk", + "partition" : "aws-iso-e", + "partitionName" : "AWS ISOE (Europe)", + "regionRegex" : "^eu\\-isoe\\-\\w+\\-\\d+$", + "regions" : { }, + "services" : { } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "csp.hci.ic.gov", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "dnsSuffix" : "csp.hci.ic.gov", + "partition" : "aws-iso-f", + "partitionName" : "AWS ISOF", + "regionRegex" : "^us\\-isof\\-\\w+\\-\\d+$", + "regions" : { }, + "services" : { } } ], "version" : 3 } diff --git a/aws/sdk/aws-models/sso.json b/aws/sdk/aws-models/sso.json index 474fc29342..4f48553e76 100644 --- a/aws/sdk/aws-models/sso.json +++ b/aws/sdk/aws-models/sso.json @@ -841,405 +841,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-northeast-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-northeast-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-northeast-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-northeast-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-northeast-3" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-northeast-3.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-southeast-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-southeast-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-southeast-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-southeast-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ca-central-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ca-central-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-central-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-central-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-north-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-north-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-3" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-3.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "me-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.me-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "sa-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.sa-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-east-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-east-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-west-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-west-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-gov-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-west-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-gov-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1275,8 +876,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1288,8 +889,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1301,8 +902,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1314,8 +915,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1327,8 +928,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1340,8 +941,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1353,8 +954,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1366,8 +967,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1379,8 +980,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1392,8 +993,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1405,8 +1006,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1418,8 +1019,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1431,8 +1032,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1444,8 +1045,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1457,8 +1058,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1470,8 +1071,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1483,8 +1084,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1496,8 +1097,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1509,8 +1110,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1522,8 +1123,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1535,8 +1136,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1548,8 +1149,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1561,8 +1162,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1574,8 +1175,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1587,8 +1188,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1600,8 +1201,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1613,8 +1214,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1626,8 +1227,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1639,8 +1240,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1652,8 +1253,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1665,8 +1266,19 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1678,8 +1290,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1691,8 +1314,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1704,8 +1338,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1717,8 +1362,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1730,8 +1375,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1743,8 +1388,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1755,8 +1400,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1767,10 +1412,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/sts.json b/aws/sdk/aws-models/sts.json index 915bf2d076..ddc251e8fa 100644 --- a/aws/sdk/aws-models/sts.json +++ b/aws/sdk/aws-models/sts.json @@ -70,7 +70,7 @@ "name": "sts" }, "aws.protocols#awsQuery": {}, - "smithy.api#documentation": "Security Token Service\n

Security Token Service (STS) enables you to request temporary, limited-privilege \n credentials for Identity and Access Management (IAM) users or for users that you \n authenticate (federated users). This guide provides descriptions of the STS API. For \n more information about using this service, see Temporary Security Credentials.

", + "smithy.api#documentation": "Security Token Service\n

Security Token Service (STS) enables you to request temporary, limited-privilege \n credentials for users. This guide provides descriptions of the STS API. For \n more information about using this service, see Temporary Security Credentials.

", "smithy.api#title": "AWS Security Token Service", "smithy.api#xmlNamespace": { "uri": "https://sts.amazonaws.com/doc/2011-06-15/" @@ -1008,8 +1008,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1021,8 +1021,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1034,8 +1034,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1047,8 +1047,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1060,8 +1060,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1073,8 +1073,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1086,8 +1086,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1099,8 +1099,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1112,8 +1112,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1134,8 +1134,8 @@ }, "params": { "Region": "aws-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1147,8 +1147,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1160,8 +1160,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1173,8 +1173,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1186,8 +1186,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1199,8 +1199,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1212,8 +1212,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1225,8 +1225,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1238,8 +1238,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1251,8 +1251,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1264,8 +1264,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1277,8 +1277,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1290,8 +1290,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1303,8 +1303,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1316,8 +1316,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1329,8 +1329,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1342,8 +1342,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1355,8 +1355,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1368,8 +1368,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1381,8 +1381,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1394,8 +1394,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1407,8 +1407,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1420,8 +1420,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1433,8 +1433,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1446,8 +1446,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1459,8 +1459,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1472,8 +1472,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1485,8 +1485,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1498,8 +1498,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1511,8 +1511,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1524,8 +1524,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1537,8 +1537,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1550,8 +1550,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1563,8 +1574,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1576,8 +1598,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1589,8 +1622,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1602,8 +1646,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1615,8 +1659,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1627,8 +1671,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1639,11 +1683,17 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } + }, { "documentation": "UseGlobalEndpoint with legacy region `ap-northeast-1`", "expect": { @@ -1651,9 +1701,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1670,10 +1720,10 @@ } ], "params": { - "Region": "ap-northeast-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "ap-northeast-1" } }, { @@ -1683,9 +1733,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1702,10 +1752,10 @@ } ], "params": { - "Region": "ap-south-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "ap-south-1" } }, { @@ -1715,9 +1765,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1734,10 +1784,10 @@ } ], "params": { - "Region": "ap-southeast-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "ap-southeast-1" } }, { @@ -1747,9 +1797,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1766,10 +1816,10 @@ } ], "params": { - "Region": "ap-southeast-2", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "ap-southeast-2" } }, { @@ -1779,9 +1829,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1798,10 +1848,10 @@ } ], "params": { - "Region": "aws-global", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "aws-global" } }, { @@ -1811,9 +1861,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1830,10 +1880,10 @@ } ], "params": { - "Region": "ca-central-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "ca-central-1" } }, { @@ -1843,9 +1893,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1862,10 +1912,10 @@ } ], "params": { - "Region": "eu-central-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "eu-central-1" } }, { @@ -1875,9 +1925,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1894,10 +1944,10 @@ } ], "params": { - "Region": "eu-north-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "eu-north-1" } }, { @@ -1907,9 +1957,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1926,10 +1976,10 @@ } ], "params": { - "Region": "eu-west-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "eu-west-1" } }, { @@ -1939,9 +1989,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1958,10 +2008,10 @@ } ], "params": { - "Region": "eu-west-2", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "eu-west-2" } }, { @@ -1971,9 +2021,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1990,10 +2040,10 @@ } ], "params": { - "Region": "eu-west-3", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "eu-west-3" } }, { @@ -2003,9 +2053,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2022,10 +2072,10 @@ } ], "params": { - "Region": "sa-east-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "sa-east-1" } }, { @@ -2035,9 +2085,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2054,10 +2104,10 @@ } ], "params": { - "Region": "us-east-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "us-east-1" } }, { @@ -2067,9 +2117,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2086,10 +2136,10 @@ } ], "params": { - "Region": "us-east-2", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "us-east-2" } }, { @@ -2099,9 +2149,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2118,10 +2168,10 @@ } ], "params": { - "Region": "us-west-1", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "us-west-1" } }, { @@ -2131,9 +2181,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-1", "signingName": "sts", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2150,10 +2200,10 @@ } ], "params": { - "Region": "us-west-2", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "us-west-2" } }, { @@ -2163,9 +2213,9 @@ "properties": { "authSchemes": [ { - "name": "sigv4", + "signingRegion": "us-east-3", "signingName": "sts", - "signingRegion": "us-east-3" + "name": "sigv4" } ] }, @@ -2182,10 +2232,10 @@ } ], "params": { - "Region": "us-east-3", - "UseFIPS": false, + "UseGlobalEndpoint": true, "UseDualStack": false, - "UseGlobalEndpoint": true + "UseFIPS": false, + "Region": "us-east-3" } }, { @@ -2206,10 +2256,10 @@ } ], "params": { - "Region": "us-west-1", - "UseFIPS": false, - "UseDualStack": false, "UseGlobalEndpoint": true, + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-west-1", "Endpoint": "https://example.com" } }, @@ -2221,10 +2271,10 @@ } }, "params": { - "UseFIPS": false, - "UseDualStack": false, + "Endpoint": "https://example.com", "UseGlobalEndpoint": false, - "Endpoint": "https://example.com" + "UseDualStack": false, + "UseFIPS": false } } ], @@ -2255,7 +2305,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a set of temporary security credentials that you can use to access Amazon Web Services\n resources. These temporary credentials consist of an access key ID, a secret access key,\n and a security token. Typically, you use AssumeRole within your account or for\n cross-account access. For a comparison of AssumeRole with other API operations\n that produce temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

\n Permissions\n

\n

The temporary security credentials created by AssumeRole can be used to\n make API calls to any Amazon Web Services service with the following exception: You cannot call the\n Amazon Web Services STS GetFederationToken or GetSessionToken API\n operations.

\n

(Optional) You can pass inline or managed session policies to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters. Passing policies to this operation returns new \n temporary credentials. The resulting session's permissions are the intersection of the \n role's identity-based policy and the session policies. You can use the role's temporary \n credentials in subsequent Amazon Web Services API calls to access resources in the account that owns \n the role. You cannot use session policies to grant more permissions than those allowed \n by the identity-based policy of the role that is being assumed. For more information, see\n Session\n Policies in the IAM User Guide.

\n

When you create a role, you create two policies: A role trust policy that specifies\n who can assume the role and a permissions policy that specifies\n what can be done with the role. You specify the trusted principal\n who is allowed to assume the role in the role trust policy.

\n

To assume a role from a different account, your Amazon Web Services account must be trusted by the\n role. The trust relationship is defined in the role's trust policy when the role is\n created. That trust policy states which accounts are allowed to delegate that access to\n users in the account.

\n

A user who wants to access a role in a different account must also have permissions that\n are delegated from the user account administrator. The administrator must attach a policy\n that allows the user to call AssumeRole for the ARN of the role in the other\n account.

\n

To allow a user to assume a role in the same account, you can do either of the\n following:

\n
    \n
  • \n

    Attach a policy to the user that allows the user to call AssumeRole\n (as long as the role's trust policy trusts the account).

    \n
  • \n
  • \n

    Add the user as a principal directly in the role's trust policy.

    \n
  • \n
\n

You can do either because the role’s trust policy acts as an IAM resource-based\n policy. When a resource-based policy grants access to a principal in the same account, no\n additional identity-based policy is required. For more information about trust policies and\n resource-based policies, see IAM Policies in the\n IAM User Guide.

\n

\n Tags\n

\n

(Optional) You can pass tag key-value pairs to your session. These tags are called\n session tags. For more information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

You can set the session tags as transitive. Transitive tags persist during role\n chaining. For more information, see Chaining Roles\n with Session Tags in the IAM User Guide.

\n

\n Using MFA with AssumeRole\n

\n

(Optional) You can include multi-factor authentication (MFA) information when you call\n AssumeRole. This is useful for cross-account scenarios to ensure that the\n user that assumes the role has been authenticated with an Amazon Web Services MFA device. In that\n scenario, the trust policy of the role being assumed includes a condition that tests for\n MFA authentication. If the caller does not include valid MFA information, the request to\n assume the role is denied. The condition in a trust policy that tests for MFA\n authentication might look like the following example.

\n

\n \"Condition\": {\"Bool\": {\"aws:MultiFactorAuthPresent\": true}}\n

\n

For more information, see Configuring MFA-Protected API Access\n in the IAM User Guide guide.

\n

To use MFA with AssumeRole, you pass values for the\n SerialNumber and TokenCode parameters. The\n SerialNumber value identifies the user's hardware or virtual MFA device.\n The TokenCode is the time-based one-time password (TOTP) that the MFA device\n produces.

" + "smithy.api#documentation": "

Returns a set of temporary security credentials that you can use to access Amazon Web Services\n resources. These temporary credentials consist of an access key ID, a secret access key,\n and a security token. Typically, you use AssumeRole within your account or for\n cross-account access. For a comparison of AssumeRole with other API operations\n that produce temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

\n Permissions\n

\n

The temporary security credentials created by AssumeRole can be used to\n make API calls to any Amazon Web Services service with the following exception: You cannot call the\n Amazon Web Services STS GetFederationToken or GetSessionToken API\n operations.

\n

(Optional) You can pass inline or managed session policies to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters. Passing policies to this operation returns new \n temporary credentials. The resulting session's permissions are the intersection of the \n role's identity-based policy and the session policies. You can use the role's temporary \n credentials in subsequent Amazon Web Services API calls to access resources in the account that owns \n the role. You cannot use session policies to grant more permissions than those allowed \n by the identity-based policy of the role that is being assumed. For more information, see\n Session\n Policies in the IAM User Guide.

\n

When you create a role, you create two policies: a role trust policy that specifies\n who can assume the role, and a permissions policy that specifies\n what can be done with the role. You specify the trusted principal\n that is allowed to assume the role in the role trust policy.

\n

To assume a role from a different account, your Amazon Web Services account must be trusted by the\n role. The trust relationship is defined in the role's trust policy when the role is\n created. That trust policy states which accounts are allowed to delegate that access to\n users in the account.

\n

A user who wants to access a role in a different account must also have permissions that\n are delegated from the account administrator. The administrator must attach a policy\n that allows the user to call AssumeRole for the ARN of the role in the other\n account.

\n

To allow a user to assume a role in the same account, you can do either of the\n following:

\n
    \n
  • \n

    Attach a policy to the user that allows the user to call AssumeRole\n (as long as the role's trust policy trusts the account).

    \n
  • \n
  • \n

    Add the user as a principal directly in the role's trust policy.

    \n
  • \n
\n

You can do either because the role’s trust policy acts as an IAM resource-based\n policy. When a resource-based policy grants access to a principal in the same account, no\n additional identity-based policy is required. For more information about trust policies and\n resource-based policies, see IAM Policies in the\n IAM User Guide.

\n

\n Tags\n

\n

(Optional) You can pass tag key-value pairs to your session. These tags are called\n session tags. For more information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

You can set the session tags as transitive. Transitive tags persist during role\n chaining. For more information, see Chaining Roles\n with Session Tags in the IAM User Guide.

\n

\n Using MFA with AssumeRole\n

\n

(Optional) You can include multi-factor authentication (MFA) information when you call\n AssumeRole. This is useful for cross-account scenarios to ensure that the\n user that assumes the role has been authenticated with an Amazon Web Services MFA device. In that\n scenario, the trust policy of the role being assumed includes a condition that tests for\n MFA authentication. If the caller does not include valid MFA information, the request to\n assume the role is denied. The condition in a trust policy that tests for MFA\n authentication might look like the following example.

\n

\n \"Condition\": {\"Bool\": {\"aws:MultiFactorAuthPresent\": true}}\n

\n

For more information, see Configuring MFA-Protected API Access\n in the IAM User Guide guide.

\n

To use MFA with AssumeRole, you pass values for the\n SerialNumber and TokenCode parameters. The\n SerialNumber value identifies the user's hardware or virtual MFA device.\n The TokenCode is the time-based one-time password (TOTP) that the MFA device\n produces.

" } }, "com.amazonaws.sts#AssumeRoleRequest": { @@ -2494,7 +2544,7 @@ "NameQualifier": { "target": "com.amazonaws.sts#NameQualifier", "traits": { - "smithy.api#documentation": "

A hash value based on the concatenation of the following:

\n
    \n
  • \n

    The Issuer response value.

    \n
  • \n
  • \n

    The Amazon Web Services account ID.

    \n
  • \n
  • \n

    The friendly name (the last part of the ARN) of the SAML provider in IAM.

    \n
  • \n
\n

The combination of NameQualifier and Subject can be used to\n uniquely identify a federated user.

\n

The following pseudocode shows how the hash value is calculated:

\n

\n BASE64 ( SHA1 ( \"https://example.com/saml\" + \"123456789012\" + \"/MySAMLIdP\" ) )\n

" + "smithy.api#documentation": "

A hash value based on the concatenation of the following:

\n
    \n
  • \n

    The Issuer response value.

    \n
  • \n
  • \n

    The Amazon Web Services account ID.

    \n
  • \n
  • \n

    The friendly name (the last part of the ARN) of the SAML provider in IAM.

    \n
  • \n
\n

The combination of NameQualifier and Subject can be used to\n uniquely identify a user.

\n

The following pseudocode shows how the hash value is calculated:

\n

\n BASE64 ( SHA1 ( \"https://example.com/saml\" + \"123456789012\" + \"/MySAMLIdP\" ) )\n

" } }, "SourceIdentity": { @@ -2541,7 +2591,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a set of temporary security credentials for users who have been authenticated in\n a mobile or web application with a web identity provider. Example providers include the\n OAuth 2.0 providers Login with Amazon and Facebook, or any OpenID Connect-compatible\n identity provider such as Google or Amazon Cognito federated identities.

\n \n

For mobile applications, we recommend that you use Amazon Cognito. You can use Amazon Cognito with the\n Amazon Web Services SDK for iOS Developer Guide and the Amazon Web Services SDK for Android Developer Guide to uniquely\n identify a user. You can also supply the user with a consistent identity throughout the\n lifetime of an application.

\n

To learn more about Amazon Cognito, see Amazon Cognito Overview in\n Amazon Web Services SDK for Android Developer Guide and Amazon Cognito Overview in the\n Amazon Web Services SDK for iOS Developer Guide.

\n
\n

Calling AssumeRoleWithWebIdentity does not require the use of Amazon Web Services\n security credentials. Therefore, you can distribute an application (for example, on mobile\n devices) that requests temporary security credentials without including long-term Amazon Web Services\n credentials in the application. You also don't need to deploy server-based proxy services\n that use long-term Amazon Web Services credentials. Instead, the identity of the caller is validated by\n using a token from the web identity provider. For a comparison of\n AssumeRoleWithWebIdentity with the other API operations that produce\n temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

The temporary security credentials returned by this API consist of an access key ID, a\n secret access key, and a security token. Applications can use these temporary security\n credentials to sign calls to Amazon Web Services service API operations.

\n

\n Session Duration\n

\n

By default, the temporary security credentials created by\n AssumeRoleWithWebIdentity last for one hour. However, you can use the\n optional DurationSeconds parameter to specify the duration of your session.\n You can provide a value from 900 seconds (15 minutes) up to the maximum session duration\n setting for the role. This setting can have a value from 1 hour to 12 hours. To learn how\n to view the maximum value for your role, see View the\n Maximum Session Duration Setting for a Role in the\n IAM User Guide. The maximum session duration limit applies when\n you use the AssumeRole* API operations or the assume-role* CLI\n commands. However the limit does not apply when you use those operations to create a\n console URL. For more information, see Using IAM Roles in the\n IAM User Guide.

\n

\n Permissions\n

\n

The temporary security credentials created by AssumeRoleWithWebIdentity can\n be used to make API calls to any Amazon Web Services service with the following exception: you cannot\n call the STS GetFederationToken or GetSessionToken API\n operations.

\n

(Optional) You can pass inline or managed session policies to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters. Passing policies to this operation returns new \n temporary credentials. The resulting session's permissions are the intersection of the \n role's identity-based policy and the session policies. You can use the role's temporary \n credentials in subsequent Amazon Web Services API calls to access resources in the account that owns \n the role. You cannot use session policies to grant more permissions than those allowed \n by the identity-based policy of the role that is being assumed. For more information, see\n Session\n Policies in the IAM User Guide.

\n

\n Tags\n

\n

(Optional) You can configure your IdP to pass attributes into your web identity token as\n session tags. Each session tag consists of a key name and an associated value. For more\n information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n

You can pass up to 50 session tags. The plaintext session tag keys can’t exceed 128\n characters and the values can’t exceed 256 characters. For these and additional limits, see\n IAM\n and STS Character Limits in the IAM User Guide.

\n \n

An Amazon Web Services conversion compresses the passed inline session policy, managed policy ARNs,\n and session tags into a packed binary format that has a separate limit. Your request can\n fail for this limit even if your plaintext meets the other requirements. The\n PackedPolicySize response element indicates by percentage how close the\n policies and tags for your request are to the upper size limit.

\n
\n

You can pass a session tag with the same key as a tag that is attached to the role. When\n you do, the session tag overrides the role tag with the same key.

\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

You can set the session tags as transitive. Transitive tags persist during role\n chaining. For more information, see Chaining Roles\n with Session Tags in the IAM User Guide.

\n

\n Identities\n

\n

Before your application can call AssumeRoleWithWebIdentity, you must have\n an identity token from a supported identity provider and create a role that the application\n can assume. The role that your application assumes must trust the identity provider that is\n associated with the identity token. In other words, the identity provider must be specified\n in the role's trust policy.

\n \n

Calling AssumeRoleWithWebIdentity can result in an entry in your\n CloudTrail logs. The entry includes the Subject of\n the provided web identity token. We recommend that you avoid using any personally\n identifiable information (PII) in this field. For example, you could instead use a GUID\n or a pairwise identifier, as suggested\n in the OIDC specification.

\n
\n

For more information about how to use web identity federation and the\n AssumeRoleWithWebIdentity API, see the following resources:

\n " + "smithy.api#documentation": "

Returns a set of temporary security credentials for users who have been authenticated in\n a mobile or web application with a web identity provider. Example providers include the\n OAuth 2.0 providers Login with Amazon and Facebook, or any OpenID Connect-compatible\n identity provider such as Google or Amazon Cognito federated identities.

\n \n

For mobile applications, we recommend that you use Amazon Cognito. You can use Amazon Cognito with the\n Amazon Web Services SDK for iOS Developer Guide and the Amazon Web Services SDK for Android Developer Guide to uniquely\n identify a user. You can also supply the user with a consistent identity throughout the\n lifetime of an application.

\n

To learn more about Amazon Cognito, see Amazon Cognito identity pools in\n Amazon Cognito Developer Guide.

\n
\n

Calling AssumeRoleWithWebIdentity does not require the use of Amazon Web Services\n security credentials. Therefore, you can distribute an application (for example, on mobile\n devices) that requests temporary security credentials without including long-term Amazon Web Services\n credentials in the application. You also don't need to deploy server-based proxy services\n that use long-term Amazon Web Services credentials. Instead, the identity of the caller is validated by\n using a token from the web identity provider. For a comparison of\n AssumeRoleWithWebIdentity with the other API operations that produce\n temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

The temporary security credentials returned by this API consist of an access key ID, a\n secret access key, and a security token. Applications can use these temporary security\n credentials to sign calls to Amazon Web Services service API operations.

\n

\n Session Duration\n

\n

By default, the temporary security credentials created by\n AssumeRoleWithWebIdentity last for one hour. However, you can use the\n optional DurationSeconds parameter to specify the duration of your session.\n You can provide a value from 900 seconds (15 minutes) up to the maximum session duration\n setting for the role. This setting can have a value from 1 hour to 12 hours. To learn how\n to view the maximum value for your role, see View the\n Maximum Session Duration Setting for a Role in the\n IAM User Guide. The maximum session duration limit applies when\n you use the AssumeRole* API operations or the assume-role* CLI\n commands. However the limit does not apply when you use those operations to create a\n console URL. For more information, see Using IAM Roles in the\n IAM User Guide.

\n

\n Permissions\n

\n

The temporary security credentials created by AssumeRoleWithWebIdentity can\n be used to make API calls to any Amazon Web Services service with the following exception: you cannot\n call the STS GetFederationToken or GetSessionToken API\n operations.

\n

(Optional) You can pass inline or managed session policies to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters. Passing policies to this operation returns new \n temporary credentials. The resulting session's permissions are the intersection of the \n role's identity-based policy and the session policies. You can use the role's temporary \n credentials in subsequent Amazon Web Services API calls to access resources in the account that owns \n the role. You cannot use session policies to grant more permissions than those allowed \n by the identity-based policy of the role that is being assumed. For more information, see\n Session\n Policies in the IAM User Guide.

\n

\n Tags\n

\n

(Optional) You can configure your IdP to pass attributes into your web identity token as\n session tags. Each session tag consists of a key name and an associated value. For more\n information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n

You can pass up to 50 session tags. The plaintext session tag keys can’t exceed 128\n characters and the values can’t exceed 256 characters. For these and additional limits, see\n IAM\n and STS Character Limits in the IAM User Guide.

\n \n

An Amazon Web Services conversion compresses the passed inline session policy, managed policy ARNs,\n and session tags into a packed binary format that has a separate limit. Your request can\n fail for this limit even if your plaintext meets the other requirements. The\n PackedPolicySize response element indicates by percentage how close the\n policies and tags for your request are to the upper size limit.

\n
\n

You can pass a session tag with the same key as a tag that is attached to the role. When\n you do, the session tag overrides the role tag with the same key.

\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

You can set the session tags as transitive. Transitive tags persist during role\n chaining. For more information, see Chaining Roles\n with Session Tags in the IAM User Guide.

\n

\n Identities\n

\n

Before your application can call AssumeRoleWithWebIdentity, you must have\n an identity token from a supported identity provider and create a role that the application\n can assume. The role that your application assumes must trust the identity provider that is\n associated with the identity token. In other words, the identity provider must be specified\n in the role's trust policy.

\n \n

Calling AssumeRoleWithWebIdentity can result in an entry in your\n CloudTrail logs. The entry includes the Subject of\n the provided web identity token. We recommend that you avoid using any personally\n identifiable information (PII) in this field. For example, you could instead use a GUID\n or a pairwise identifier, as suggested\n in the OIDC specification.

\n
\n

For more information about how to use web identity federation and the\n AssumeRoleWithWebIdentity API, see the following resources:

\n " } }, "com.amazonaws.sts#AssumeRoleWithWebIdentityRequest": { @@ -2845,7 +2895,7 @@ "target": "com.amazonaws.sts#GetCallerIdentityResponse" }, "traits": { - "smithy.api#documentation": "

Returns details about the IAM user or role whose credentials are used to call the\n operation.

\n \n

No permissions are required to perform this operation. If an administrator adds a\n policy to your IAM user or role that explicitly denies access to the\n sts:GetCallerIdentity action, you can still perform this operation.\n Permissions are not required because the same information is returned when an IAM user\n or role is denied access. To view an example response, see I Am Not Authorized to Perform: iam:DeleteVirtualMFADevice in the\n IAM User Guide.

\n
" + "smithy.api#documentation": "

Returns details about the IAM user or role whose credentials are used to call the operation.

\n \n

No permissions are required to perform this operation. If an administrator\n attaches a policy to your identity that explicitly denies access to the\n sts:GetCallerIdentity action, you can still perform this operation.\n Permissions are not required because the same information is returned when access is denied. To view an example response, see I Am Not Authorized to Perform: iam:DeleteVirtualMFADevice in the\n IAM User Guide.

\n
" } }, "com.amazonaws.sts#GetCallerIdentityRequest": { @@ -2902,7 +2952,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a set of temporary security credentials (consisting of an access key ID, a\n secret access key, and a security token) for a federated user. A typical use is in a proxy\n application that gets temporary security credentials on behalf of distributed applications\n inside a corporate network. You must call the GetFederationToken operation\n using the long-term security credentials of an IAM user. As a result, this call is\n appropriate in contexts where those credentials can be safely stored, usually in a\n server-based application. For a comparison of GetFederationToken with the\n other API operations that produce temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n \n

You can create a mobile-based or browser-based app that can authenticate users using\n a web identity provider like Login with Amazon, Facebook, Google, or an OpenID\n Connect-compatible identity provider. In this case, we recommend that you use Amazon Cognito or\n AssumeRoleWithWebIdentity. For more information, see Federation Through a Web-based Identity Provider in the\n IAM User Guide.

\n
\n

You can also call GetFederationToken using the security credentials of an\n Amazon Web Services account root user, but we do not recommend it. Instead, we recommend that you create\n an IAM user for the purpose of the proxy application. Then attach a policy to the IAM\n user that limits federated users to only the actions and resources that they need to\n access. For more information, see IAM Best Practices in the\n IAM User Guide.

\n

\n Session duration\n

\n

The temporary credentials are valid for the specified duration, from 900 seconds (15\n minutes) up to a maximum of 129,600 seconds (36 hours). The default session duration is\n 43,200 seconds (12 hours). Temporary credentials obtained by using the Amazon Web Services account root\n user credentials have a maximum duration of 3,600 seconds (1 hour).

\n

\n Permissions\n

\n

You can use the temporary credentials created by GetFederationToken in any\n Amazon Web Services service with the following exceptions:

\n
    \n
  • \n

    You cannot call any IAM operations using the CLI or the Amazon Web Services API. This limitation does not apply to console sessions.

    \n
  • \n
  • \n

    You cannot call any STS operations except GetCallerIdentity.

    \n
  • \n
\n

You can use temporary credentials for single sign-on (SSO) to the console.

\n

You must pass an inline or managed session policy to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters.

\n

Though the session policy parameters are optional, if you do not pass a policy, then the\n resulting federated user session has no permissions. When you pass session policies, the\n session permissions are the intersection of the IAM user policies and the session\n policies that you pass. This gives you a way to further restrict the permissions for a\n federated user. You cannot use session policies to grant more permissions than those that\n are defined in the permissions policy of the IAM user. For more information, see Session\n Policies in the IAM User Guide. For information about\n using GetFederationToken to create temporary security credentials, see GetFederationToken—Federation Through a Custom Identity Broker.

\n

You can use the credentials to access a resource that has a resource-based policy. If\n that policy specifically references the federated user session in the\n Principal element of the policy, the session has the permissions allowed by\n the policy. These permissions are granted in addition to the permissions granted by the\n session policies.

\n

\n Tags\n

\n

(Optional) You can pass tag key-value pairs to your session. These are called session\n tags. For more information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n \n

You can create a mobile-based or browser-based app that can authenticate users using\n a web identity provider like Login with Amazon, Facebook, Google, or an OpenID\n Connect-compatible identity provider. In this case, we recommend that you use Amazon Cognito or\n AssumeRoleWithWebIdentity. For more information, see Federation Through a Web-based Identity Provider in the\n IAM User Guide.

\n
\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

Tag key–value pairs are not case sensitive, but case is preserved. This means that you\n cannot have separate Department and department tag keys. Assume\n that the user that you are federating has the\n Department=Marketing tag and you pass the\n department=engineering session tag. Department\n and department are not saved as separate tags, and the session tag passed in\n the request takes precedence over the user tag.

" + "smithy.api#documentation": "

Returns a set of temporary security credentials (consisting of an access key ID, a\n secret access key, and a security token) for a user. A typical use is in a proxy\n application that gets temporary security credentials on behalf of distributed applications\n inside a corporate network.

\n

You must call the GetFederationToken operation\n using the long-term security credentials of an IAM user. As a result, this call is\n appropriate in contexts where those credentials can be safeguarded, usually in a\n server-based application. For a comparison of GetFederationToken with the\n other API operations that produce temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

Although it is possible to call GetFederationToken using the security credentials of an\n Amazon Web Services account root user rather than an IAM user that you create for the purpose of a proxy application, we do not recommend it. For more information, see Safeguard your root user credentials and don't use them for everyday tasks in the\n IAM User Guide.

\n \n

You can create a mobile-based or browser-based app that can authenticate users using\n a web identity provider like Login with Amazon, Facebook, Google, or an OpenID\n Connect-compatible identity provider. In this case, we recommend that you use Amazon Cognito or\n AssumeRoleWithWebIdentity. For more information, see Federation Through a Web-based Identity Provider in the\n IAM User Guide.

\n
\n

\n Session duration\n

\n

The temporary credentials are valid for the specified duration, from 900 seconds (15\n minutes) up to a maximum of 129,600 seconds (36 hours). The default session duration is\n 43,200 seconds (12 hours). Temporary credentials obtained by using the root user credentials have a maximum duration of 3,600 seconds (1 hour).

\n

\n Permissions\n

\n

You can use the temporary credentials created by GetFederationToken in any\n Amazon Web Services service with the following exceptions:

\n
    \n
  • \n

    You cannot call any IAM operations using the CLI or the Amazon Web Services API. This limitation does not apply to console sessions.

    \n
  • \n
  • \n

    You cannot call any STS operations except GetCallerIdentity.

    \n
  • \n
\n

You can use temporary credentials for single sign-on (SSO) to the console.

\n

You must pass an inline or managed session policy to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters.

\n

Though the session policy parameters are optional, if you do not pass a policy, then the\n resulting federated user session has no permissions. When you pass session policies, the\n session permissions are the intersection of the IAM user policies and the session\n policies that you pass. This gives you a way to further restrict the permissions for a\n federated user. You cannot use session policies to grant more permissions than those that\n are defined in the permissions policy of the IAM user. For more information, see Session\n Policies in the IAM User Guide. For information about\n using GetFederationToken to create temporary security credentials, see GetFederationToken—Federation Through a Custom Identity Broker.

\n

You can use the credentials to access a resource that has a resource-based policy. If\n that policy specifically references the federated user session in the\n Principal element of the policy, the session has the permissions allowed by\n the policy. These permissions are granted in addition to the permissions granted by the\n session policies.

\n

\n Tags\n

\n

(Optional) You can pass tag key-value pairs to your session. These are called session\n tags. For more information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n \n

You can create a mobile-based or browser-based app that can authenticate users using\n a web identity provider like Login with Amazon, Facebook, Google, or an OpenID\n Connect-compatible identity provider. In this case, we recommend that you use Amazon Cognito or\n AssumeRoleWithWebIdentity. For more information, see Federation Through a Web-based Identity Provider in the\n IAM User Guide.

\n
\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

Tag key–value pairs are not case sensitive, but case is preserved. This means that you\n cannot have separate Department and department tag keys. Assume\n that the user that you are federating has the\n Department=Marketing tag and you pass the\n department=engineering session tag. Department\n and department are not saved as separate tags, and the session tag passed in\n the request takes precedence over the user tag.

" } }, "com.amazonaws.sts#GetFederationTokenRequest": { @@ -2930,7 +2980,7 @@ "DurationSeconds": { "target": "com.amazonaws.sts#durationSecondsType", "traits": { - "smithy.api#documentation": "

The duration, in seconds, that the session should last. Acceptable durations for\n federation sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with\n 43,200 seconds (12 hours) as the default. Sessions obtained using Amazon Web Services account root user\n credentials are restricted to a maximum of 3,600 seconds (one hour). If the specified\n duration is longer than one hour, the session obtained by using root user credentials\n defaults to one hour.

" + "smithy.api#documentation": "

The duration, in seconds, that the session should last. Acceptable durations for\n federation sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with\n 43,200 seconds (12 hours) as the default. Sessions obtained using root user\n credentials are restricted to a maximum of 3,600 seconds (one hour). If the specified\n duration is longer than one hour, the session obtained by using root user credentials\n defaults to one hour.

" } }, "Tags": { @@ -2985,7 +3035,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a set of temporary credentials for an Amazon Web Services account or IAM user. The\n credentials consist of an access key ID, a secret access key, and a security token.\n Typically, you use GetSessionToken if you want to use MFA to protect\n programmatic calls to specific Amazon Web Services API operations like Amazon EC2 StopInstances.\n MFA-enabled IAM users would need to call GetSessionToken and submit an MFA\n code that is associated with their MFA device. Using the temporary security credentials\n that are returned from the call, IAM users can then make programmatic calls to API\n operations that require MFA authentication. If you do not supply a correct MFA code, then\n the API returns an access denied error. For a comparison of GetSessionToken\n with the other API operations that produce temporary credentials, see Requesting\n Temporary Security Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n \n

No permissions are required for users to perform this operation. The purpose of the\n sts:GetSessionToken operation is to authenticate the user using MFA. You\n cannot use policies to control authentication operations. For more information, see\n Permissions for GetSessionToken in the\n IAM User Guide.

\n
\n

\n Session Duration\n

\n

The GetSessionToken operation must be called by using the long-term Amazon Web Services\n security credentials of the Amazon Web Services account root user or an IAM user. Credentials that are\n created by IAM users are valid for the duration that you specify. This duration can range\n from 900 seconds (15 minutes) up to a maximum of 129,600 seconds (36 hours), with a default\n of 43,200 seconds (12 hours). Credentials based on account credentials can range from 900\n seconds (15 minutes) up to 3,600 seconds (1 hour), with a default of 1 hour.

\n

\n Permissions\n

\n

The temporary security credentials created by GetSessionToken can be used\n to make API calls to any Amazon Web Services service with the following exceptions:

\n
    \n
  • \n

    You cannot call any IAM API operations unless MFA authentication information is\n included in the request.

    \n
  • \n
  • \n

    You cannot call any STS API except\n AssumeRole or GetCallerIdentity.

    \n
  • \n
\n \n

We recommend that you do not call GetSessionToken with Amazon Web Services account\n root user credentials. Instead, follow our best practices by\n creating one or more IAM users, giving them the necessary permissions, and using IAM\n users for everyday interaction with Amazon Web Services.

\n
\n

The credentials that are returned by GetSessionToken are based on\n permissions associated with the user whose credentials were used to call the operation. If\n GetSessionToken is called using Amazon Web Services account root user credentials, the\n temporary credentials have root user permissions. Similarly, if\n GetSessionToken is called using the credentials of an IAM user, the\n temporary credentials have the same permissions as the IAM user.

\n

For more information about using GetSessionToken to create temporary\n credentials, go to Temporary\n Credentials for Users in Untrusted Environments in the\n IAM User Guide.

" + "smithy.api#documentation": "

Returns a set of temporary credentials for an Amazon Web Services account or IAM user. The\n credentials consist of an access key ID, a secret access key, and a security token.\n Typically, you use GetSessionToken if you want to use MFA to protect\n programmatic calls to specific Amazon Web Services API operations like Amazon EC2 StopInstances.

\n

MFA-enabled IAM users must call GetSessionToken and submit an MFA\n code that is associated with their MFA device. Using the temporary security credentials\n that the call returns, IAM users can then make programmatic calls to API\n operations that require MFA authentication. An incorrect MFA code causes the API to return an access denied error. For a comparison of GetSessionToken\n with the other API operations that produce temporary credentials, see Requesting\n Temporary Security Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n \n

No permissions are required for users to perform this operation. The purpose of the\n sts:GetSessionToken operation is to authenticate the user using MFA. You\n cannot use policies to control authentication operations. For more information, see\n Permissions for GetSessionToken in the\n IAM User Guide.

\n
\n

\n Session Duration\n

\n

The GetSessionToken operation must be called by using the long-term Amazon Web Services\n security credentials of an IAM user. Credentials that are\n created by IAM users are valid for the duration that you specify. This duration can range\n from 900 seconds (15 minutes) up to a maximum of 129,600 seconds (36 hours), with a default\n of 43,200 seconds (12 hours). Credentials based on account credentials can range from 900\n seconds (15 minutes) up to 3,600 seconds (1 hour), with a default of 1 hour.

\n

\n Permissions\n

\n

The temporary security credentials created by GetSessionToken can be used\n to make API calls to any Amazon Web Services service with the following exceptions:

\n
    \n
  • \n

    You cannot call any IAM API operations unless MFA authentication information is\n included in the request.

    \n
  • \n
  • \n

    You cannot call any STS API except\n AssumeRole or GetCallerIdentity.

    \n
  • \n
\n

The credentials that GetSessionToken returns are based on\n permissions associated with the IAM user whose credentials were used to call the operation. The\n temporary credentials have the same permissions as the IAM user.

\n \n

Although it is possible to call GetSessionToken using the security credentials of an\n Amazon Web Services account root user rather than an IAM user, we do not recommend it. If\n GetSessionToken is called using root user credentials, the\n temporary credentials have root user permissions. For more information, see Safeguard your root user credentials and don't use them for everyday tasks in the\n IAM User Guide\n

\n
\n

For more information about using GetSessionToken to create temporary\n credentials, see Temporary\n Credentials for Users in Untrusted Environments in the\n IAM User Guide.

" } }, "com.amazonaws.sts#GetSessionTokenRequest": { @@ -3174,7 +3224,8 @@ "smithy.api#length": { "min": 4, "max": 100000 - } + }, + "smithy.api#sensitive": {} } }, "com.amazonaws.sts#Subject": { @@ -3216,7 +3267,10 @@ } }, "com.amazonaws.sts#accessKeySecretType": { - "type": "string" + "type": "string", + "traits": { + "smithy.api#sensitive": {} + } }, "com.amazonaws.sts#accountType": { "type": "string" @@ -3247,7 +3301,8 @@ "smithy.api#length": { "min": 4, "max": 20000 - } + }, + "smithy.api#sensitive": {} } }, "com.amazonaws.sts#dateType": { diff --git a/aws/sdk/aws-models/timestream-query.json b/aws/sdk/aws-models/timestream-query.json new file mode 100644 index 0000000000..29edeadd2a --- /dev/null +++ b/aws/sdk/aws-models/timestream-query.json @@ -0,0 +1,3097 @@ +{ + "smithy": "2.0", + "metadata": { + "suppressions": [ + { + "id": "HttpMethodSemantics", + "namespace": "*" + }, + { + "id": "HttpResponseCodeSemantics", + "namespace": "*" + }, + { + "id": "PaginatedTrait", + "namespace": "*" + }, + { + "id": "HttpHeaderTrait", + "namespace": "*" + }, + { + "id": "HttpUriConflict", + "namespace": "*" + }, + { + "id": "Service", + "namespace": "*" + } + ] + }, + "shapes": { + "com.amazonaws.timestreamquery#AccessDeniedException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ServiceErrorMessage" + } + }, + "traits": { + "aws.protocols#awsQueryError": { + "code": "AccessDenied", + "httpResponseCode": 403 + }, + "smithy.api#documentation": "

You are not authorized to perform this action.

", + "smithy.api#error": "client", + "smithy.api#httpError": 403 + } + }, + "com.amazonaws.timestreamquery#AmazonResourceName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + } + } + }, + "com.amazonaws.timestreamquery#CancelQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#CancelQueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#CancelQueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Cancels a query that has been issued. Cancellation is provided only if the query has\n not completed running before the cancellation request was issued. Because cancellation\n is an idempotent operation, subsequent cancellation requests will return a\n CancellationMessage, indicating that the query has already been\n canceled. See code\n sample for details.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#CancelQueryRequest": { + "type": "structure", + "members": { + "QueryId": { + "target": "com.amazonaws.timestreamquery#QueryId", + "traits": { + "smithy.api#documentation": "

The ID of the query that needs to be cancelled. QueryID is returned as\n part of the query result.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#CancelQueryResponse": { + "type": "structure", + "members": { + "CancellationMessage": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

A CancellationMessage is returned when a CancelQuery\n request for the query specified by QueryId has already been issued.

" + } + } + } + }, + "com.amazonaws.timestreamquery#ClientRequestToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 32, + "max": 128 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.timestreamquery#ClientToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 32, + "max": 128 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.timestreamquery#ColumnInfo": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

The name of the result set column. The name of the result set is available for\n columns of all data types except for arrays.

" + } + }, + "Type": { + "target": "com.amazonaws.timestreamquery#Type", + "traits": { + "smithy.api#documentation": "

The data type of the result set column. The data type can be a scalar or complex.\n Scalar data types are integers, strings, doubles, Booleans, and others. Complex data\n types are types such as arrays, rows, and others.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the metadata for query results such as the column names, data types, and\n other attributes.

" + } + }, + "com.amazonaws.timestreamquery#ColumnInfoList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#ColumnInfo" + } + }, + "com.amazonaws.timestreamquery#ConflictException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

Unable to poll results for a cancelled query.

", + "smithy.api#error": "client", + "smithy.api#httpError": 409 + } + }, + "com.amazonaws.timestreamquery#CreateScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#CreateScheduledQueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#CreateScheduledQueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#ConflictException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Create a scheduled query that will be run on your behalf at the configured schedule.\n Timestream assumes the execution role provided as part of the\n ScheduledQueryExecutionRoleArn parameter to run the query. You can use\n the NotificationConfiguration parameter to configure notification for your\n scheduled query operations.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#CreateScheduledQueryRequest": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryName", + "traits": { + "smithy.api#documentation": "

Name of the scheduled query.

", + "smithy.api#required": {} + } + }, + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The query string to run. Parameter\n names can be specified in the query string @ character followed by an\n identifier. The named Parameter @scheduled_runtime is reserved and can be used in the query to get the time at which the query is scheduled to run.

\n

The timestamp calculated according to the ScheduleConfiguration parameter, will be the value of @scheduled_runtime paramater for each query run. \n For example, consider an instance of a scheduled query executing on 2021-12-01 00:00:00. For this instance, the @scheduled_runtime parameter is \n initialized to the timestamp 2021-12-01 00:00:00 when invoking the query.

", + "smithy.api#required": {} + } + }, + "ScheduleConfiguration": { + "target": "com.amazonaws.timestreamquery#ScheduleConfiguration", + "traits": { + "smithy.api#documentation": "

The schedule configuration for the query.

", + "smithy.api#required": {} + } + }, + "NotificationConfiguration": { + "target": "com.amazonaws.timestreamquery#NotificationConfiguration", + "traits": { + "smithy.api#documentation": "

Notification configuration for the scheduled query. A notification is sent by\n Timestream when a query run finishes, when the state is updated or when you delete it.

", + "smithy.api#required": {} + } + }, + "TargetConfiguration": { + "target": "com.amazonaws.timestreamquery#TargetConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration used for writing the result of a query.

" + } + }, + "ClientToken": { + "target": "com.amazonaws.timestreamquery#ClientToken", + "traits": { + "smithy.api#documentation": "

Using a ClientToken makes the call to CreateScheduledQuery idempotent, in other words, making the same request repeatedly will produce the same result. Making \n multiple identical CreateScheduledQuery requests has the same effect as making a single request.\n\n

\n
    \n
  • \n

    If CreateScheduledQuery is called without a ClientToken, the\n Query SDK generates a ClientToken on your behalf.

    \n
  • \n
  • \n

    After 8 hours, any request with the same ClientToken is treated\n as a new request.

    \n
  • \n
", + "smithy.api#idempotencyToken": {} + } + }, + "ScheduledQueryExecutionRoleArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The ARN for the IAM role that Timestream will assume when running the scheduled query.

", + "smithy.api#required": {} + } + }, + "Tags": { + "target": "com.amazonaws.timestreamquery#TagList", + "traits": { + "smithy.api#documentation": "

A list of key-value pairs to label the scheduled query.

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamquery#StringValue2048", + "traits": { + "smithy.api#documentation": "

The Amazon KMS key used to encrypt the scheduled query resource, at-rest. If the Amazon KMS\n key is not specified, the scheduled query resource will be encrypted with a Timestream\n owned Amazon KMS key. To specify a KMS key, use the key ID, key ARN, alias name, or alias\n ARN. When using an alias name, prefix the name with alias/\n

\n

If ErrorReportConfiguration uses SSE_KMS as encryption type, the same KmsKeyId is used to encrypt the error report at rest.

" + } + }, + "ErrorReportConfiguration": { + "target": "com.amazonaws.timestreamquery#ErrorReportConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration for error reporting. Error reports will be generated when a problem is encountered when writing the query results.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#CreateScheduledQueryResponse": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

ARN for the created scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#Datum": { + "type": "structure", + "members": { + "ScalarValue": { + "target": "com.amazonaws.timestreamquery#ScalarValue", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is a scalar value such as integer, string, double, or\n Boolean.

" + } + }, + "TimeSeriesValue": { + "target": "com.amazonaws.timestreamquery#TimeSeriesDataPointList", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is a timeseries data type.

" + } + }, + "ArrayValue": { + "target": "com.amazonaws.timestreamquery#DatumList", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is an array.

" + } + }, + "RowValue": { + "target": "com.amazonaws.timestreamquery#Row", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is a row.

" + } + }, + "NullValue": { + "target": "com.amazonaws.timestreamquery#NullableBoolean", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is null.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Datum represents a single data point in a query result.

" + } + }, + "com.amazonaws.timestreamquery#DatumList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#Datum" + } + }, + "com.amazonaws.timestreamquery#DeleteScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#DeleteScheduledQueryRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Deletes a given scheduled query. This is an irreversible operation.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#DeleteScheduledQueryRequest": { + "type": "structure", + "members": { + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The ARN of the scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#DescribeEndpoints": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#DescribeEndpointsRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#DescribeEndpointsResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

DescribeEndpoints returns a list of available endpoints to make Timestream\n API calls against. This API is available through both Write and Query.

\n

Because the Timestream SDKs are designed to transparently work with the\n service’s architecture, including the management and mapping of the service endpoints,\n it is not recommended that you use this API unless:

\n \n

For detailed information on how and when to use and implement DescribeEndpoints, see\n The Endpoint Discovery Pattern.

" + } + }, + "com.amazonaws.timestreamquery#DescribeEndpointsRequest": { + "type": "structure", + "members": {} + }, + "com.amazonaws.timestreamquery#DescribeEndpointsResponse": { + "type": "structure", + "members": { + "Endpoints": { + "target": "com.amazonaws.timestreamquery#Endpoints", + "traits": { + "smithy.api#documentation": "

An Endpoints object is returned when a DescribeEndpoints\n request is made.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#DescribeScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#DescribeScheduledQueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#DescribeScheduledQueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Provides detailed information about a scheduled query.

" + } + }, + "com.amazonaws.timestreamquery#DescribeScheduledQueryRequest": { + "type": "structure", + "members": { + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The ARN of the scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#DescribeScheduledQueryResponse": { + "type": "structure", + "members": { + "ScheduledQuery": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryDescription", + "traits": { + "smithy.api#documentation": "

The scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#DimensionMapping": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Column name from query result.

", + "smithy.api#required": {} + } + }, + "DimensionValueType": { + "target": "com.amazonaws.timestreamquery#DimensionValueType", + "traits": { + "smithy.api#documentation": "

Type for the dimension.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

This type is used to map column(s) from the query result to a dimension in the\n destination table.

" + } + }, + "com.amazonaws.timestreamquery#DimensionMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#DimensionMapping" + } + }, + "com.amazonaws.timestreamquery#DimensionValueType": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "VARCHAR", + "name": "VARCHAR" + } + ] + } + }, + "com.amazonaws.timestreamquery#Double": { + "type": "double", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamquery#Endpoint": { + "type": "structure", + "members": { + "Address": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

An endpoint address.

", + "smithy.api#required": {} + } + }, + "CachePeriodInMinutes": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The TTL for the endpoint, in minutes.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an available endpoint against which to make API calls against, as well as\n the TTL for that endpoint.

" + } + }, + "com.amazonaws.timestreamquery#Endpoints": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#Endpoint" + } + }, + "com.amazonaws.timestreamquery#ErrorMessage": { + "type": "string" + }, + "com.amazonaws.timestreamquery#ErrorReportConfiguration": { + "type": "structure", + "members": { + "S3Configuration": { + "target": "com.amazonaws.timestreamquery#S3Configuration", + "traits": { + "smithy.api#documentation": "

The S3 configuration for the error reports.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration required for error reporting.

" + } + }, + "com.amazonaws.timestreamquery#ErrorReportLocation": { + "type": "structure", + "members": { + "S3ReportLocation": { + "target": "com.amazonaws.timestreamquery#S3ReportLocation", + "traits": { + "smithy.api#documentation": "

The S3 location where error reports are written.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

This contains the location of the error report for a single scheduled query call.\n

" + } + }, + "com.amazonaws.timestreamquery#ExecuteScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#ExecuteScheduledQueryRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

You can use this API to run a scheduled query manually.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#ExecuteScheduledQueryRequest": { + "type": "structure", + "members": { + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

ARN of the scheduled query.

", + "smithy.api#required": {} + } + }, + "InvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The timestamp in UTC. Query will be run as if it was invoked at this timestamp.

", + "smithy.api#required": {} + } + }, + "ClientToken": { + "target": "com.amazonaws.timestreamquery#ClientToken", + "traits": { + "smithy.api#documentation": "

Not used.

", + "smithy.api#idempotencyToken": {} + } + } + } + }, + "com.amazonaws.timestreamquery#ExecutionStats": { + "type": "structure", + "members": { + "ExecutionTimeInMillis": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Total time, measured in milliseconds, that was needed for the scheduled query run to complete.

" + } + }, + "DataWrites": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Data writes metered for records ingested in a single scheduled query run.

" + } + }, + "BytesMetered": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Bytes metered for a single scheduled query run.

" + } + }, + "RecordsIngested": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The number of records ingested for a single scheduled query run.

" + } + }, + "QueryResultRows": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Number of rows present in the output from running a query before ingestion to\n destination data source.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Statistics for a single scheduled query run.

" + } + }, + "com.amazonaws.timestreamquery#InternalServerException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

\n Timestream was unable to fully process this request because of an internal\n server error.

", + "smithy.api#error": "server", + "smithy.api#httpError": 500 + } + }, + "com.amazonaws.timestreamquery#InvalidEndpointException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The requested endpoint was not valid.

", + "smithy.api#error": "client", + "smithy.api#httpError": 421 + } + }, + "com.amazonaws.timestreamquery#ListScheduledQueries": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#ListScheduledQueriesRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#ListScheduledQueriesResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Gets a list of all scheduled queries in the caller's Amazon account and Region. ListScheduledQueries is eventually consistent.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamquery#ListScheduledQueriesRequest": { + "type": "structure", + "members": { + "MaxResults": { + "target": "com.amazonaws.timestreamquery#MaxScheduledQueriesResults", + "traits": { + "smithy.api#documentation": "

The maximum number of items to return in the output. If the total number of items\n available is more than the value specified, a NextToken is provided in the\n output. To resume pagination, provide the NextToken value as the argument\n to the subsequent call to ListScheduledQueriesRequest.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#NextScheduledQueriesResultsToken", + "traits": { + "smithy.api#documentation": "

A pagination token to resume pagination.

" + } + } + } + }, + "com.amazonaws.timestreamquery#ListScheduledQueriesResponse": { + "type": "structure", + "members": { + "ScheduledQueries": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryList", + "traits": { + "smithy.api#documentation": "

A list of scheduled queries.

", + "smithy.api#required": {} + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#NextScheduledQueriesResultsToken", + "traits": { + "smithy.api#documentation": "

A token to specify where to start paginating. This is the NextToken from a previously\n truncated response.

" + } + } + } + }, + "com.amazonaws.timestreamquery#ListTagsForResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#ListTagsForResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#ListTagsForResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

List all tags on a Timestream query resource.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamquery#ListTagsForResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Timestream resource with tags to be listed. This value is an Amazon Resource Name\n (ARN).

", + "smithy.api#required": {} + } + }, + "MaxResults": { + "target": "com.amazonaws.timestreamquery#MaxTagsForResourceResult", + "traits": { + "smithy.api#documentation": "

The maximum number of tags to return.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#NextTagsForResourceResultsToken", + "traits": { + "smithy.api#documentation": "

A pagination token to resume pagination.

" + } + } + } + }, + "com.amazonaws.timestreamquery#ListTagsForResourceResponse": { + "type": "structure", + "members": { + "Tags": { + "target": "com.amazonaws.timestreamquery#TagList", + "traits": { + "smithy.api#documentation": "

The tags currently associated with the Timestream resource.

", + "smithy.api#required": {} + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#NextTagsForResourceResultsToken", + "traits": { + "smithy.api#documentation": "

A pagination token to resume pagination with a subsequent call to\n ListTagsForResourceResponse.

" + } + } + } + }, + "com.amazonaws.timestreamquery#Long": { + "type": "long", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamquery#MaxQueryResults": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 1000 + } + } + }, + "com.amazonaws.timestreamquery#MaxScheduledQueriesResults": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 1000 + } + } + }, + "com.amazonaws.timestreamquery#MaxTagsForResourceResult": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 200 + } + } + }, + "com.amazonaws.timestreamquery#MeasureValueType": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "BIGINT", + "name": "BIGINT" + }, + { + "value": "BOOLEAN", + "name": "BOOLEAN" + }, + { + "value": "DOUBLE", + "name": "DOUBLE" + }, + { + "value": "VARCHAR", + "name": "VARCHAR" + }, + { + "value": "MULTI", + "name": "MULTI" + } + ] + } + }, + "com.amazonaws.timestreamquery#MixedMeasureMapping": { + "type": "structure", + "members": { + "MeasureName": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Refers to the value of measure_name in a result row. This field is required if\n MeasureNameColumn is provided.

" + } + }, + "SourceColumn": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

This field refers to the source column from which measure-value is to be read for\n result materialization.

" + } + }, + "TargetMeasureName": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Target measure name to be used. If not provided, the target measure name by default\n would be measure-name if provided, or sourceColumn otherwise.

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamquery#MeasureValueType", + "traits": { + "smithy.api#documentation": "

Type of the value that is to be read from sourceColumn. If the mapping is for MULTI,\n use MeasureValueType.MULTI.

", + "smithy.api#required": {} + } + }, + "MultiMeasureAttributeMappings": { + "target": "com.amazonaws.timestreamquery#MultiMeasureAttributeMappingList", + "traits": { + "smithy.api#documentation": "

Required when measureValueType is MULTI. Attribute mappings for MULTI value\n measures.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

MixedMeasureMappings are mappings that can be used to ingest data into a mixture of\n narrow and multi measures in the derived table.

" + } + }, + "com.amazonaws.timestreamquery#MixedMeasureMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#MixedMeasureMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamquery#MultiMeasureAttributeMapping": { + "type": "structure", + "members": { + "SourceColumn": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Source column from where the attribute value is to be read.

", + "smithy.api#required": {} + } + }, + "TargetMultiMeasureAttributeName": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Custom name to be used for attribute name in derived table. If not provided, source\n column name would be used.

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamquery#ScalarMeasureValueType", + "traits": { + "smithy.api#documentation": "

Type of the attribute to be read from the source column.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Attribute mapping for MULTI value measures.

" + } + }, + "com.amazonaws.timestreamquery#MultiMeasureAttributeMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#MultiMeasureAttributeMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamquery#MultiMeasureMappings": { + "type": "structure", + "members": { + "TargetMultiMeasureName": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

The name of the target multi-measure name in the derived table. This input is required\n when measureNameColumn is not provided. If MeasureNameColumn is provided, then value\n from that column will be used as multi-measure name.

" + } + }, + "MultiMeasureAttributeMappings": { + "target": "com.amazonaws.timestreamquery#MultiMeasureAttributeMappingList", + "traits": { + "smithy.api#documentation": "

Required. Attribute mappings to be used for mapping query results to ingest data for\n multi-measure attributes.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Only one of MixedMeasureMappings or MultiMeasureMappings is to be provided.\n MultiMeasureMappings can be used to ingest data as multi measures in the derived\n table.

" + } + }, + "com.amazonaws.timestreamquery#NextScheduledQueriesResultsToken": { + "type": "string" + }, + "com.amazonaws.timestreamquery#NextTagsForResourceResultsToken": { + "type": "string" + }, + "com.amazonaws.timestreamquery#NotificationConfiguration": { + "type": "structure", + "members": { + "SnsConfiguration": { + "target": "com.amazonaws.timestreamquery#SnsConfiguration", + "traits": { + "smithy.api#documentation": "

Details on SNS configuration.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Notification configuration for a scheduled query. A notification is sent by\n Timestream when a scheduled query is created, its state is updated or when it is deleted.

" + } + }, + "com.amazonaws.timestreamquery#NullableBoolean": { + "type": "boolean" + }, + "com.amazonaws.timestreamquery#PaginationToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + } + } + }, + "com.amazonaws.timestreamquery#ParameterMapping": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

Parameter name.

", + "smithy.api#required": {} + } + }, + "Type": { + "target": "com.amazonaws.timestreamquery#Type", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Mapping for named parameters.

" + } + }, + "com.amazonaws.timestreamquery#ParameterMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#ParameterMapping" + } + }, + "com.amazonaws.timestreamquery#PrepareQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#PrepareQueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#PrepareQueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

A synchronous operation that allows you to submit a query with parameters to be stored\n by Timestream for later running. Timestream only supports using this operation with the\n PrepareQueryRequest$ValidateOnly set to true.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#PrepareQueryRequest": { + "type": "structure", + "members": { + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The Timestream query string that you want to use as a prepared statement. Parameter\n names can be specified in the query string @ character followed by an\n identifier.

", + "smithy.api#required": {} + } + }, + "ValidateOnly": { + "target": "com.amazonaws.timestreamquery#NullableBoolean", + "traits": { + "smithy.api#documentation": "

By setting this value to true, Timestream will only validate that the\n query string is a valid Timestream query, and not store the prepared query for later\n use.

" + } + } + } + }, + "com.amazonaws.timestreamquery#PrepareQueryResponse": { + "type": "structure", + "members": { + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The query string that you want prepare.

", + "smithy.api#required": {} + } + }, + "Columns": { + "target": "com.amazonaws.timestreamquery#SelectColumnList", + "traits": { + "smithy.api#documentation": "

A list of SELECT clause columns of the submitted query string.

", + "smithy.api#required": {} + } + }, + "Parameters": { + "target": "com.amazonaws.timestreamquery#ParameterMappingList", + "traits": { + "smithy.api#documentation": "

A list of parameters used in the submitted query string.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#Query": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#QueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#QueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#ConflictException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#QueryExecutionException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

\n Query is a synchronous operation that enables you to run a query against\n your Amazon Timestream data. Query will time out after 60 seconds.\n You must update the default timeout in the SDK to support a timeout of 60 seconds. See\n the code\n sample for details.

\n

Your query request will fail in the following cases:

\n
    \n
  • \n

    If you submit a Query request with the same client token outside\n of the 5-minute idempotency window.

    \n
  • \n
  • \n

    If you submit a Query request with the same client token, but\n change other parameters, within the 5-minute idempotency window.

    \n
  • \n
  • \n

    If the size of the row (including the query metadata) exceeds 1 MB, then the\n query will fail with the following error message:

    \n

    \n Query aborted as max page response size has been exceeded by the output\n result row\n

    \n
  • \n
  • \n

    If the IAM principal of the query initiator and the result reader are not the\n same and/or the query initiator and the result reader do not have the same query\n string in the query requests, the query will fail with an Invalid\n pagination token error.

    \n
  • \n
", + "smithy.api#idempotent": {}, + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "items": "Rows", + "pageSize": "MaxRows" + } + } + }, + "com.amazonaws.timestreamquery#QueryExecutionException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

\n Timestream was unable to run the query successfully.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.timestreamquery#QueryId": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 64 + }, + "smithy.api#pattern": "^[a-zA-Z0-9]+$" + } + }, + "com.amazonaws.timestreamquery#QueryRequest": { + "type": "structure", + "members": { + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The query to be run by Timestream.

", + "smithy.api#required": {} + } + }, + "ClientToken": { + "target": "com.amazonaws.timestreamquery#ClientRequestToken", + "traits": { + "smithy.api#documentation": "

Unique, case-sensitive string of up to 64 ASCII characters specified when a\n Query request is made. Providing a ClientToken makes the\n call to Query\n idempotent. This means that running the same query repeatedly will\n produce the same result. In other words, making multiple identical Query\n requests has the same effect as making a single request. When using\n ClientToken in a query, note the following:

\n
    \n
  • \n

    If the Query API is instantiated without a ClientToken, the\n Query SDK generates a ClientToken on your behalf.

    \n
  • \n
  • \n

    If the Query invocation only contains the\n ClientToken but does not include a NextToken, that\n invocation of Query is assumed to be a new query run.

    \n
  • \n
  • \n

    If the invocation contains NextToken, that particular invocation\n is assumed to be a subsequent invocation of a prior call to the Query API, and a\n result set is returned.

    \n
  • \n
  • \n

    After 4 hours, any request with the same ClientToken is treated\n as a new request.

    \n
  • \n
", + "smithy.api#idempotencyToken": {} + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#PaginationToken", + "traits": { + "smithy.api#documentation": "

A pagination token used to return a set of results. When the Query API\n is invoked using NextToken, that particular invocation is assumed to be a\n subsequent invocation of a prior call to Query, and a result set is\n returned. However, if the Query invocation only contains the\n ClientToken, that invocation of Query is assumed to be a\n new query run.

\n

Note the following when using NextToken in a query:

\n
    \n
  • \n

    A pagination token can be used for up to five Query invocations,\n OR for a duration of up to 1 hour – whichever comes first.

    \n
  • \n
  • \n

    Using the same NextToken will return the same set of records. To\n keep paginating through the result set, you must to use the most recent\n nextToken.

    \n
  • \n
  • \n

    Suppose a Query invocation returns two NextToken\n values, TokenA and TokenB. If TokenB is\n used in a subsequent Query invocation, then TokenA is\n invalidated and cannot be reused.

    \n
  • \n
  • \n

    To request a previous result set from a query after pagination has begun, you\n must re-invoke the Query API.

    \n
  • \n
  • \n

    The latest NextToken should be used to paginate until\n null is returned, at which point a new NextToken\n should be used.

    \n
  • \n
  • \n

    If the IAM principal of the query initiator and the result reader are not the\n same and/or the query initiator and the result reader do not have the same query\n string in the query requests, the query will fail with an Invalid\n pagination token error.

    \n
  • \n
" + } + }, + "MaxRows": { + "target": "com.amazonaws.timestreamquery#MaxQueryResults", + "traits": { + "smithy.api#documentation": "

The total number of rows to be returned in the Query output. The initial\n run of Query with a MaxRows value specified will return the\n result set of the query in two cases:

\n
    \n
  • \n

    The size of the result is less than 1MB.

    \n
  • \n
  • \n

    The number of rows in the result set is less than the value of\n maxRows.

    \n
  • \n
\n

Otherwise, the initial invocation of Query only returns a\n NextToken, which can then be used in subsequent calls to fetch the\n result set. To resume pagination, provide the NextToken value in the\n subsequent command.

\n

If the row size is large (e.g. a row has many columns), Timestream may return\n fewer rows to keep the response size from exceeding the 1 MB limit. If\n MaxRows is not provided, Timestream will send the necessary\n number of rows to meet the 1 MB limit.

" + } + } + } + }, + "com.amazonaws.timestreamquery#QueryResponse": { + "type": "structure", + "members": { + "QueryId": { + "target": "com.amazonaws.timestreamquery#QueryId", + "traits": { + "smithy.api#documentation": "

A unique ID for the given query.

", + "smithy.api#required": {} + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#PaginationToken", + "traits": { + "smithy.api#documentation": "

A pagination token that can be used again on a Query call to get the\n next set of results.

" + } + }, + "Rows": { + "target": "com.amazonaws.timestreamquery#RowList", + "traits": { + "smithy.api#documentation": "

The result set rows returned by the query.

", + "smithy.api#required": {} + } + }, + "ColumnInfo": { + "target": "com.amazonaws.timestreamquery#ColumnInfoList", + "traits": { + "smithy.api#documentation": "

The column data types of the returned result set.

", + "smithy.api#required": {} + } + }, + "QueryStatus": { + "target": "com.amazonaws.timestreamquery#QueryStatus", + "traits": { + "smithy.api#documentation": "

Information about the status of the query, including progress and bytes\n scanned.

" + } + } + } + }, + "com.amazonaws.timestreamquery#QueryStatus": { + "type": "structure", + "members": { + "ProgressPercentage": { + "target": "com.amazonaws.timestreamquery#Double", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The progress of the query, expressed as a percentage.

" + } + }, + "CumulativeBytesScanned": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The amount of data scanned by the query in bytes. This is a cumulative sum and\n represents the total amount of bytes scanned since the query was started.

" + } + }, + "CumulativeBytesMetered": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The amount of data scanned by the query in bytes that you will be charged for. This is\n a cumulative sum and represents the total amount of data that you will be charged for\n since the query was started. The charge is applied only once and is either applied when\n the query completes running or when the query is cancelled.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Information about the status of the query, including progress and bytes\n scanned.

" + } + }, + "com.amazonaws.timestreamquery#QueryString": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 262144 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.timestreamquery#ResourceName": { + "type": "string" + }, + "com.amazonaws.timestreamquery#ResourceNotFoundException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + }, + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The ARN of the scheduled query.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The requested resource could not be found.

", + "smithy.api#error": "client", + "smithy.api#httpError": 404 + } + }, + "com.amazonaws.timestreamquery#Row": { + "type": "structure", + "members": { + "Data": { + "target": "com.amazonaws.timestreamquery#DatumList", + "traits": { + "smithy.api#documentation": "

List of data points in a single row of the result set.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a single row in the query results.

" + } + }, + "com.amazonaws.timestreamquery#RowList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#Row" + } + }, + "com.amazonaws.timestreamquery#S3BucketName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 3, + "max": 63 + }, + "smithy.api#pattern": "^[a-z0-9][\\.\\-a-z0-9]{1,61}[a-z0-9]$" + } + }, + "com.amazonaws.timestreamquery#S3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamquery#S3BucketName", + "traits": { + "smithy.api#documentation": "

Name of the S3 bucket under which error reports will be created.

", + "smithy.api#required": {} + } + }, + "ObjectKeyPrefix": { + "target": "com.amazonaws.timestreamquery#S3ObjectKeyPrefix", + "traits": { + "smithy.api#documentation": "

Prefix for the error report key. Timestream by default adds the following prefix to\n the error report path.

" + } + }, + "EncryptionOption": { + "target": "com.amazonaws.timestreamquery#S3EncryptionOption", + "traits": { + "smithy.api#documentation": "

Encryption at rest options for the error reports. If no encryption option is\n specified, Timestream will choose SSE_S3 as default.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details on S3 location for error reports that result from running a query.

" + } + }, + "com.amazonaws.timestreamquery#S3EncryptionOption": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "SSE_S3", + "name": "SSE_S3" + }, + { + "value": "SSE_KMS", + "name": "SSE_KMS" + } + ] + } + }, + "com.amazonaws.timestreamquery#S3ObjectKey": { + "type": "string" + }, + "com.amazonaws.timestreamquery#S3ObjectKeyPrefix": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 896 + }, + "smithy.api#pattern": "^[a-zA-Z0-9|!\\-_*'\\(\\)]([a-zA-Z0-9]|[!\\-_*'\\(\\)\\/.])+$" + } + }, + "com.amazonaws.timestreamquery#S3ReportLocation": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamquery#S3BucketName", + "traits": { + "smithy.api#documentation": "

S3 bucket name.

" + } + }, + "ObjectKey": { + "target": "com.amazonaws.timestreamquery#S3ObjectKey", + "traits": { + "smithy.api#documentation": "

S3 key.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

S3 report location for the scheduled query run.

" + } + }, + "com.amazonaws.timestreamquery#ScalarMeasureValueType": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "BIGINT", + "name": "BIGINT" + }, + { + "value": "BOOLEAN", + "name": "BOOLEAN" + }, + { + "value": "DOUBLE", + "name": "DOUBLE" + }, + { + "value": "VARCHAR", + "name": "VARCHAR" + }, + { + "value": "TIMESTAMP", + "name": "TIMESTAMP" + } + ] + } + }, + "com.amazonaws.timestreamquery#ScalarType": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "VARCHAR", + "name": "VARCHAR" + }, + { + "value": "BOOLEAN", + "name": "BOOLEAN" + }, + { + "value": "BIGINT", + "name": "BIGINT" + }, + { + "value": "DOUBLE", + "name": "DOUBLE" + }, + { + "value": "TIMESTAMP", + "name": "TIMESTAMP" + }, + { + "value": "DATE", + "name": "DATE" + }, + { + "value": "TIME", + "name": "TIME" + }, + { + "value": "INTERVAL_DAY_TO_SECOND", + "name": "INTERVAL_DAY_TO_SECOND" + }, + { + "value": "INTERVAL_YEAR_TO_MONTH", + "name": "INTERVAL_YEAR_TO_MONTH" + }, + { + "value": "UNKNOWN", + "name": "UNKNOWN" + }, + { + "value": "INTEGER", + "name": "INTEGER" + } + ] + } + }, + "com.amazonaws.timestreamquery#ScalarValue": { + "type": "string" + }, + "com.amazonaws.timestreamquery#ScheduleConfiguration": { + "type": "structure", + "members": { + "ScheduleExpression": { + "target": "com.amazonaws.timestreamquery#ScheduleExpression", + "traits": { + "smithy.api#documentation": "

An expression that denotes when to trigger the scheduled query run. This can be a cron\n expression or a rate expression.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration of the schedule of the query.

" + } + }, + "com.amazonaws.timestreamquery#ScheduleExpression": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 256 + } + } + }, + "com.amazonaws.timestreamquery#ScheduledQuery": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name.

", + "smithy.api#required": {} + } + }, + "Name": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryName", + "traits": { + "smithy.api#documentation": "

The name of the scheduled query.

", + "smithy.api#required": {} + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The creation time of the scheduled query.

" + } + }, + "State": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryState", + "traits": { + "smithy.api#documentation": "

State of scheduled query.

", + "smithy.api#required": {} + } + }, + "PreviousInvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The last time the scheduled query was run.

" + } + }, + "NextInvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The next time the scheduled query is to be run.

" + } + }, + "ErrorReportConfiguration": { + "target": "com.amazonaws.timestreamquery#ErrorReportConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration for scheduled query error reporting.

" + } + }, + "TargetDestination": { + "target": "com.amazonaws.timestreamquery#TargetDestination", + "traits": { + "smithy.api#documentation": "

Target data source where final scheduled query result will be written.

" + } + }, + "LastRunStatus": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunStatus", + "traits": { + "smithy.api#documentation": "

Status of the last scheduled query run.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Scheduled Query

" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryDescription": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

Scheduled query ARN.

", + "smithy.api#required": {} + } + }, + "Name": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryName", + "traits": { + "smithy.api#documentation": "

Name of the scheduled query.

", + "smithy.api#required": {} + } + }, + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The query to be run.

", + "smithy.api#required": {} + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

Creation time of the scheduled query.

" + } + }, + "State": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryState", + "traits": { + "smithy.api#documentation": "

State of the scheduled query.

", + "smithy.api#required": {} + } + }, + "PreviousInvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

Last time the query was run.

" + } + }, + "NextInvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The next time the scheduled query is scheduled to run.

" + } + }, + "ScheduleConfiguration": { + "target": "com.amazonaws.timestreamquery#ScheduleConfiguration", + "traits": { + "smithy.api#documentation": "

Schedule configuration.

", + "smithy.api#required": {} + } + }, + "NotificationConfiguration": { + "target": "com.amazonaws.timestreamquery#NotificationConfiguration", + "traits": { + "smithy.api#documentation": "

Notification configuration.

", + "smithy.api#required": {} + } + }, + "TargetConfiguration": { + "target": "com.amazonaws.timestreamquery#TargetConfiguration", + "traits": { + "smithy.api#documentation": "

Scheduled query target store configuration.

" + } + }, + "ScheduledQueryExecutionRoleArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

IAM role that Timestream uses to run the schedule query.

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamquery#StringValue2048", + "traits": { + "smithy.api#documentation": "

A customer provided KMS key used to encrypt the scheduled query resource.

" + } + }, + "ErrorReportConfiguration": { + "target": "com.amazonaws.timestreamquery#ErrorReportConfiguration", + "traits": { + "smithy.api#documentation": "

Error-reporting configuration for the scheduled query.

" + } + }, + "LastRunSummary": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunSummary", + "traits": { + "smithy.api#documentation": "

Runtime summary for the last scheduled query run.

" + } + }, + "RecentlyFailedRuns": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunSummaryList", + "traits": { + "smithy.api#documentation": "

Runtime summary for the last five failed scheduled query runs.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Structure that describes scheduled query.

" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#ScheduledQuery" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 64 + }, + "smithy.api#pattern": "^[a-zA-Z0-9_.-]+$" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryRunStatus": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "AUTO_TRIGGER_SUCCESS", + "name": "AUTO_TRIGGER_SUCCESS" + }, + { + "value": "AUTO_TRIGGER_FAILURE", + "name": "AUTO_TRIGGER_FAILURE" + }, + { + "value": "MANUAL_TRIGGER_SUCCESS", + "name": "MANUAL_TRIGGER_SUCCESS" + }, + { + "value": "MANUAL_TRIGGER_FAILURE", + "name": "MANUAL_TRIGGER_FAILURE" + } + ] + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryRunSummary": { + "type": "structure", + "members": { + "InvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

InvocationTime for this run. This is the time at which the query is scheduled to run.\n Parameter @scheduled_runtime can be used in the query to get the value.

" + } + }, + "TriggerTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The actual time when the query was run.

" + } + }, + "RunStatus": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunStatus", + "traits": { + "smithy.api#documentation": "

The status of a scheduled query run.

" + } + }, + "ExecutionStats": { + "target": "com.amazonaws.timestreamquery#ExecutionStats", + "traits": { + "smithy.api#documentation": "

Runtime statistics for a scheduled run.

" + } + }, + "ErrorReportLocation": { + "target": "com.amazonaws.timestreamquery#ErrorReportLocation", + "traits": { + "smithy.api#documentation": "

S3 location for error report.

" + } + }, + "FailureReason": { + "target": "com.amazonaws.timestreamquery#ErrorMessage", + "traits": { + "smithy.api#documentation": "

Error message for the scheduled query in case of failure. You might have to look at\n the error report to get more detailed error reasons.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Run summary for the scheduled query

" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryRunSummaryList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunSummary" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryState": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "ENABLED", + "name": "ENABLED" + }, + { + "value": "DISABLED", + "name": "DISABLED" + } + ] + } + }, + "com.amazonaws.timestreamquery#SchemaName": { + "type": "string" + }, + "com.amazonaws.timestreamquery#SelectColumn": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

Name of the column.

" + } + }, + "Type": { + "target": "com.amazonaws.timestreamquery#Type" + }, + "DatabaseName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Database that has this column.

" + } + }, + "TableName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Table within the database that has this column.

" + } + }, + "Aliased": { + "target": "com.amazonaws.timestreamquery#NullableBoolean", + "traits": { + "smithy.api#documentation": "

True, if the column name was aliased by the query. False otherwise.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details of the column that is returned by the query.

" + } + }, + "com.amazonaws.timestreamquery#SelectColumnList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#SelectColumn" + } + }, + "com.amazonaws.timestreamquery#ServiceErrorMessage": { + "type": "string" + }, + "com.amazonaws.timestreamquery#ServiceQuotaExceededException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

You have exceeded the service quota.

", + "smithy.api#error": "client", + "smithy.api#httpError": 402 + } + }, + "com.amazonaws.timestreamquery#SnsConfiguration": { + "type": "structure", + "members": { + "TopicArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

SNS topic ARN that the scheduled query status notifications will be sent to.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Details on SNS that are required to send the notification.

" + } + }, + "com.amazonaws.timestreamquery#String": { + "type": "string" + }, + "com.amazonaws.timestreamquery#StringValue2048": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + } + } + }, + "com.amazonaws.timestreamquery#Tag": { + "type": "structure", + "members": { + "Key": { + "target": "com.amazonaws.timestreamquery#TagKey", + "traits": { + "smithy.api#documentation": "

The key of the tag. Tag keys are case sensitive.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamquery#TagValue", + "traits": { + "smithy.api#documentation": "

The value of the tag. Tag values are case sensitive and can be null.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A tag is a label that you assign to a Timestream database and/or table. Each tag\n consists of a key and an optional value, both of which you define. Tags enable you to\n categorize databases and/or tables, for example, by purpose, owner, or environment.\n

" + } + }, + "com.amazonaws.timestreamquery#TagKey": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 128 + } + } + }, + "com.amazonaws.timestreamquery#TagKeyList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#TagKey" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.timestreamquery#TagList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#Tag" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.timestreamquery#TagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#TagResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#TagResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Associate a set of tags with a Timestream resource. You can then activate these\n user-defined tags so that they appear on the Billing and Cost Management console for\n cost allocation tracking.

" + } + }, + "com.amazonaws.timestreamquery#TagResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

Identifies the Timestream resource to which tags should be added. This value is an\n Amazon Resource Name (ARN).

", + "smithy.api#required": {} + } + }, + "Tags": { + "target": "com.amazonaws.timestreamquery#TagList", + "traits": { + "smithy.api#documentation": "

The tags to be assigned to the Timestream resource.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#TagResourceResponse": { + "type": "structure", + "members": {} + }, + "com.amazonaws.timestreamquery#TagValue": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 0, + "max": 256 + } + } + }, + "com.amazonaws.timestreamquery#TargetConfiguration": { + "type": "structure", + "members": { + "TimestreamConfiguration": { + "target": "com.amazonaws.timestreamquery#TimestreamConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration needed to write data into the Timestream database and table.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration used for writing the output of a query.

" + } + }, + "com.amazonaws.timestreamquery#TargetDestination": { + "type": "structure", + "members": { + "TimestreamDestination": { + "target": "com.amazonaws.timestreamquery#TimestreamDestination", + "traits": { + "smithy.api#documentation": "

Query result destination details for Timestream data source.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Destination details to write data for a target data source. Current supported data\n source is Timestream.

" + } + }, + "com.amazonaws.timestreamquery#ThrottlingException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The request was denied due to request throttling.

", + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.timestreamquery#Time": { + "type": "timestamp" + }, + "com.amazonaws.timestreamquery#TimeSeriesDataPoint": { + "type": "structure", + "members": { + "Time": { + "target": "com.amazonaws.timestreamquery#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the measure value was collected.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamquery#Datum", + "traits": { + "smithy.api#documentation": "

The measure value for the data point.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The timeseries data type represents the values of a measure over time. A time series\n is an array of rows of timestamps and measure values, with rows sorted in ascending\n order of time. A TimeSeriesDataPoint is a single data point in the time series. It\n represents a tuple of (time, measure value) in a time series.

" + } + }, + "com.amazonaws.timestreamquery#TimeSeriesDataPointList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#TimeSeriesDataPoint" + } + }, + "com.amazonaws.timestreamquery#Timestamp": { + "type": "string" + }, + "com.amazonaws.timestreamquery#TimestreamConfiguration": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Name of Timestream database to which the query result will be written.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Name of Timestream table that the query result will be written to. The table should\n be within the same database that is provided in Timestream configuration.

", + "smithy.api#required": {} + } + }, + "TimeColumn": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Column from query result that should be used as the time column in destination table.\n Column type for this should be TIMESTAMP.

", + "smithy.api#required": {} + } + }, + "DimensionMappings": { + "target": "com.amazonaws.timestreamquery#DimensionMappingList", + "traits": { + "smithy.api#documentation": "

This is to allow mapping column(s) from the query result to the dimension in the\n destination table.

", + "smithy.api#required": {} + } + }, + "MultiMeasureMappings": { + "target": "com.amazonaws.timestreamquery#MultiMeasureMappings", + "traits": { + "smithy.api#documentation": "

Multi-measure mappings.

" + } + }, + "MixedMeasureMappings": { + "target": "com.amazonaws.timestreamquery#MixedMeasureMappingList", + "traits": { + "smithy.api#documentation": "

Specifies how to map measures to multi-measure records.

" + } + }, + "MeasureNameColumn": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Name of the measure column.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration to write data into Timestream database and table. This configuration\n allows the user to map the query result select columns into the destination table\n columns.

" + } + }, + "com.amazonaws.timestreamquery#TimestreamDestination": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Timestream database name.

" + } + }, + "TableName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Timestream table name.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Destination for scheduled query.

" + } + }, + "com.amazonaws.timestreamquery#Timestream_20181101": { + "type": "service", + "version": "2018-11-01", + "operations": [ + { + "target": "com.amazonaws.timestreamquery#CancelQuery" + }, + { + "target": "com.amazonaws.timestreamquery#CreateScheduledQuery" + }, + { + "target": "com.amazonaws.timestreamquery#DeleteScheduledQuery" + }, + { + "target": "com.amazonaws.timestreamquery#DescribeEndpoints" + }, + { + "target": "com.amazonaws.timestreamquery#DescribeScheduledQuery" + }, + { + "target": "com.amazonaws.timestreamquery#ExecuteScheduledQuery" + }, + { + "target": "com.amazonaws.timestreamquery#ListScheduledQueries" + }, + { + "target": "com.amazonaws.timestreamquery#ListTagsForResource" + }, + { + "target": "com.amazonaws.timestreamquery#PrepareQuery" + }, + { + "target": "com.amazonaws.timestreamquery#Query" + }, + { + "target": "com.amazonaws.timestreamquery#TagResource" + }, + { + "target": "com.amazonaws.timestreamquery#UntagResource" + }, + { + "target": "com.amazonaws.timestreamquery#UpdateScheduledQuery" + } + ], + "traits": { + "aws.api#clientEndpointDiscovery": { + "operation": "com.amazonaws.timestreamquery#DescribeEndpoints", + "error": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + "aws.api#service": { + "sdkId": "Timestream Query", + "arnNamespace": "timestream", + "cloudFormationName": "TimestreamQuery", + "cloudTrailEventSource": "timestreamquery.amazonaws.com", + "endpointPrefix": "query.timestream" + }, + "aws.auth#sigv4": { + "name": "timestream" + }, + "aws.protocols#awsJson1_0": {}, + "smithy.api#documentation": "Amazon Timestream Query\n \n

", + "smithy.api#title": "Amazon Timestream Query", + "smithy.rules#endpointRuleSet": { + "version": "1.0", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": false, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "Boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "Boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Region" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://query.timestream-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://query.timestream-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://query.timestream.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://query.timestream.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" + } + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips enabled and dualstack disabled", + "expect": { + "error": "Invalid Configuration: FIPS and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack enabled", + "expect": { + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } + } + ], + "version": "1.0" + } + } + }, + "com.amazonaws.timestreamquery#Type": { + "type": "structure", + "members": { + "ScalarType": { + "target": "com.amazonaws.timestreamquery#ScalarType", + "traits": { + "smithy.api#documentation": "

Indicates if the column is of type string, integer, Boolean, double, timestamp, date,\n time.

" + } + }, + "ArrayColumnInfo": { + "target": "com.amazonaws.timestreamquery#ColumnInfo", + "traits": { + "smithy.api#documentation": "

Indicates if the column is an array.

" + } + }, + "TimeSeriesMeasureValueColumnInfo": { + "target": "com.amazonaws.timestreamquery#ColumnInfo", + "traits": { + "smithy.api#documentation": "

Indicates if the column is a timeseries data type.

" + } + }, + "RowColumnInfo": { + "target": "com.amazonaws.timestreamquery#ColumnInfoList", + "traits": { + "smithy.api#documentation": "

Indicates if the column is a row.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the data type of a column in a query result set. The data type can be scalar\n or complex. The supported scalar data types are integers, Boolean, string, double,\n timestamp, date, time, and intervals. The supported complex data types are arrays, rows,\n and timeseries.

" + } + }, + "com.amazonaws.timestreamquery#UntagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#UntagResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#UntagResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Removes the association of tags from a Timestream query resource.

" + } + }, + "com.amazonaws.timestreamquery#UntagResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Timestream resource that the tags will be removed from. This value is an Amazon\n Resource Name (ARN).

", + "smithy.api#required": {} + } + }, + "TagKeys": { + "target": "com.amazonaws.timestreamquery#TagKeyList", + "traits": { + "smithy.api#documentation": "

A list of tags keys. Existing tags of the resource whose keys are members of this list\n will be removed from the Timestream resource.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#UntagResourceResponse": { + "type": "structure", + "members": {} + }, + "com.amazonaws.timestreamquery#UpdateScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#UpdateScheduledQueryRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Update a scheduled query.

" + } + }, + "com.amazonaws.timestreamquery#UpdateScheduledQueryRequest": { + "type": "structure", + "members": { + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

ARN of the scheuled query.

", + "smithy.api#required": {} + } + }, + "State": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryState", + "traits": { + "smithy.api#documentation": "

State of the scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#ValidationException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

Invalid or malformed request.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + } + } +} diff --git a/aws/sdk/aws-models/timestream-write.json b/aws/sdk/aws-models/timestream-write.json new file mode 100644 index 0000000000..a3378d042b --- /dev/null +++ b/aws/sdk/aws-models/timestream-write.json @@ -0,0 +1,3817 @@ +{ + "smithy": "2.0", + "metadata": { + "suppressions": [ + { + "id": "HttpMethodSemantics", + "namespace": "*" + }, + { + "id": "HttpResponseCodeSemantics", + "namespace": "*" + }, + { + "id": "PaginatedTrait", + "namespace": "*" + }, + { + "id": "HttpHeaderTrait", + "namespace": "*" + }, + { + "id": "HttpUriConflict", + "namespace": "*" + }, + { + "id": "Service", + "namespace": "*" + } + ] + }, + "shapes": { + "com.amazonaws.timestreamwrite#AccessDeniedException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

You are not authorized to perform this action.

", + "smithy.api#error": "client", + "smithy.api#httpError": 403 + } + }, + "com.amazonaws.timestreamwrite#AmazonResourceName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 1011 + } + } + }, + "com.amazonaws.timestreamwrite#BatchLoadDataFormat": { + "type": "enum", + "members": { + "CSV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CSV" + } + } + } + }, + "com.amazonaws.timestreamwrite#BatchLoadProgressReport": { + "type": "structure", + "members": { + "RecordsProcessed": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "RecordsIngested": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "ParseFailures": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "RecordIngestionFailures": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "FileFailures": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "BytesMetered": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details about the progress of a batch load task.

" + } + }, + "com.amazonaws.timestreamwrite#BatchLoadStatus": { + "type": "enum", + "members": { + "CREATED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CREATED" + } + }, + "IN_PROGRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "IN_PROGRESS" + } + }, + "FAILED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "FAILED" + } + }, + "SUCCEEDED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SUCCEEDED" + } + }, + "PROGRESS_STOPPED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PROGRESS_STOPPED" + } + }, + "PENDING_RESUME": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PENDING_RESUME" + } + } + } + }, + "com.amazonaws.timestreamwrite#BatchLoadTask": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task.

" + } + }, + "TaskStatus": { + "target": "com.amazonaws.timestreamwrite#BatchLoadStatus", + "traits": { + "smithy.api#documentation": "

Status of the batch load task.

" + } + }, + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

Database name for the database into which a batch load task loads data.

" + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

Table name for the table into which a batch load task loads data.

" + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream batch load task was created.

" + } + }, + "LastUpdatedTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream batch load task was last updated.

" + } + }, + "ResumableUntil": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details about a batch load task.

" + } + }, + "com.amazonaws.timestreamwrite#BatchLoadTaskDescription": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task.

" + } + }, + "ErrorMessage": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

" + } + }, + "DataSourceConfiguration": { + "target": "com.amazonaws.timestreamwrite#DataSourceConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration details about the data source for a batch load task.

" + } + }, + "ProgressReport": { + "target": "com.amazonaws.timestreamwrite#BatchLoadProgressReport", + "traits": { + "smithy.api#documentation": "

" + } + }, + "ReportConfiguration": { + "target": "com.amazonaws.timestreamwrite#ReportConfiguration", + "traits": { + "smithy.api#documentation": "

Report configuration for a batch load task. This contains details about where error\n reports are stored.

" + } + }, + "DataModelConfiguration": { + "target": "com.amazonaws.timestreamwrite#DataModelConfiguration", + "traits": { + "smithy.api#documentation": "

Data model configuration for a batch load task. This contains details about where a data\n model for a batch load task is stored.

" + } + }, + "TargetDatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "TargetTableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "TaskStatus": { + "target": "com.amazonaws.timestreamwrite#BatchLoadStatus", + "traits": { + "smithy.api#documentation": "

Status of the batch load task.

" + } + }, + "RecordVersion": { + "target": "com.amazonaws.timestreamwrite#RecordVersion", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream batch load task was created.

" + } + }, + "LastUpdatedTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream batch load task was last updated.

" + } + }, + "ResumableUntil": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details about a batch load task.

" + } + }, + "com.amazonaws.timestreamwrite#BatchLoadTaskId": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 3, + "max": 32 + }, + "smithy.api#pattern": "^[A-Z0-9]+$" + } + }, + "com.amazonaws.timestreamwrite#BatchLoadTaskList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTask" + } + }, + "com.amazonaws.timestreamwrite#Boolean": { + "type": "boolean" + }, + "com.amazonaws.timestreamwrite#ClientRequestToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 64 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.timestreamwrite#ConflictException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Timestream was unable to process this request because it contains resource that\n already exists.

", + "smithy.api#error": "client", + "smithy.api#httpError": 409 + } + }, + "com.amazonaws.timestreamwrite#CreateBatchLoadTask": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#CreateBatchLoadTaskRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#CreateBatchLoadTaskResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#ConflictException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Creates a new Timestream batch load task. A batch load task processes data from\n a CSV source in an S3 location and writes to a Timestream table. A mapping from\n source to target is defined in a batch load task. Errors and events are written to a report\n at an S3 location. For the report, if the KMS key is not specified, the\n report will be encrypted with an S3 managed key when SSE_S3 is the option.\n Otherwise an error is thrown. For more information, see Amazon Web Services managed\n keys. Service quotas apply. For\n details, see code\n sample.

" + } + }, + "com.amazonaws.timestreamwrite#CreateBatchLoadTaskRequest": { + "type": "structure", + "members": { + "ClientToken": { + "target": "com.amazonaws.timestreamwrite#ClientRequestToken", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#idempotencyToken": {} + } + }, + "DataModelConfiguration": { + "target": "com.amazonaws.timestreamwrite#DataModelConfiguration" + }, + "DataSourceConfiguration": { + "target": "com.amazonaws.timestreamwrite#DataSourceConfiguration", + "traits": { + "smithy.api#documentation": "

Defines configuration details about the data source for a batch load task.

", + "smithy.api#required": {} + } + }, + "ReportConfiguration": { + "target": "com.amazonaws.timestreamwrite#ReportConfiguration", + "traits": { + "smithy.api#required": {} + } + }, + "TargetDatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

Target Timestream database for a batch load task.

", + "smithy.api#required": {} + } + }, + "TargetTableName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

Target Timestream table for a batch load task.

", + "smithy.api#required": {} + } + }, + "RecordVersion": { + "target": "com.amazonaws.timestreamwrite#RecordVersion", + "traits": { + "smithy.api#default": null, + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#CreateBatchLoadTaskResponse": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#CreateDatabase": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#CreateDatabaseRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#CreateDatabaseResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#ConflictException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Creates a new Timestream database. If the KMS key is not\n specified, the database will be encrypted with a Timestream managed KMS key located in your account. For more information, see Amazon Web Services managed keys. Service quotas apply. For\n details, see code sample.\n

" + } + }, + "com.amazonaws.timestreamwrite#CreateDatabaseRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The KMS key for the database. If the KMS key is not\n specified, the database will be encrypted with a Timestream managed KMS key located in your account. For more information, see Amazon Web Services managed keys.

" + } + }, + "Tags": { + "target": "com.amazonaws.timestreamwrite#TagList", + "traits": { + "smithy.api#documentation": "

A list of key-value pairs to label the table.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#CreateDatabaseResponse": { + "type": "structure", + "members": { + "Database": { + "target": "com.amazonaws.timestreamwrite#Database", + "traits": { + "smithy.api#documentation": "

The newly created Timestream database.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#CreateTable": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#CreateTableRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#CreateTableResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#ConflictException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Adds a new table to an existing database in your account. In an Amazon Web Services account, table names must be at least unique within each Region if they are in the same\n database. You might have identical table names in the same Region if the tables are in\n separate databases. While creating the table, you must specify the table name, database\n name, and the retention properties. Service quotas apply. See\n code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#CreateTableRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

", + "smithy.api#required": {} + } + }, + "RetentionProperties": { + "target": "com.amazonaws.timestreamwrite#RetentionProperties", + "traits": { + "smithy.api#documentation": "

The duration for which your time-series data must be stored in the memory store and the\n magnetic store.

" + } + }, + "Tags": { + "target": "com.amazonaws.timestreamwrite#TagList", + "traits": { + "smithy.api#documentation": "

A list of key-value pairs to label the table.

" + } + }, + "MagneticStoreWriteProperties": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreWriteProperties", + "traits": { + "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" + } + }, + "Schema": { + "target": "com.amazonaws.timestreamwrite#Schema", + "traits": { + "smithy.api#documentation": "

The schema of the table.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#CreateTableResponse": { + "type": "structure", + "members": { + "Table": { + "target": "com.amazonaws.timestreamwrite#Table", + "traits": { + "smithy.api#documentation": "

The newly created Timestream table.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#CsvConfiguration": { + "type": "structure", + "members": { + "ColumnSeparator": { + "target": "com.amazonaws.timestreamwrite#StringValue1", + "traits": { + "smithy.api#documentation": "

Column separator can be one of comma (','), pipe ('|), semicolon (';'), tab('/t'), or\n blank space (' ').

" + } + }, + "EscapeChar": { + "target": "com.amazonaws.timestreamwrite#StringValue1", + "traits": { + "smithy.api#documentation": "

Escape character can be one of

" + } + }, + "QuoteChar": { + "target": "com.amazonaws.timestreamwrite#StringValue1", + "traits": { + "smithy.api#documentation": "

Can be single quote (') or double quote (\").

" + } + }, + "NullValue": { + "target": "com.amazonaws.timestreamwrite#StringValue256", + "traits": { + "smithy.api#documentation": "

Can be blank space (' ').

" + } + }, + "TrimWhiteSpace": { + "target": "com.amazonaws.timestreamwrite#Boolean", + "traits": { + "smithy.api#documentation": "

Specifies to trim leading and trailing white space.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A delimited data format where the column separator can be a comma and the record\n separator is a newline character.

" + } + }, + "com.amazonaws.timestreamwrite#DataModel": { + "type": "structure", + "members": { + "TimeColumn": { + "target": "com.amazonaws.timestreamwrite#StringValue256", + "traits": { + "smithy.api#documentation": "

Source column to be mapped to time.

" + } + }, + "TimeUnit": { + "target": "com.amazonaws.timestreamwrite#TimeUnit", + "traits": { + "smithy.api#documentation": "

The granularity of the timestamp unit. It indicates if the time value is in seconds,\n milliseconds, nanoseconds, or other supported values. Default is MILLISECONDS.\n

" + } + }, + "DimensionMappings": { + "target": "com.amazonaws.timestreamwrite#DimensionMappings", + "traits": { + "smithy.api#documentation": "

Source to target mappings for dimensions.

", + "smithy.api#required": {} + } + }, + "MultiMeasureMappings": { + "target": "com.amazonaws.timestreamwrite#MultiMeasureMappings", + "traits": { + "smithy.api#documentation": "

Source to target mappings for multi-measure records.

" + } + }, + "MixedMeasureMappings": { + "target": "com.amazonaws.timestreamwrite#MixedMeasureMappingList", + "traits": { + "smithy.api#documentation": "

Source to target mappings for measures.

" + } + }, + "MeasureNameColumn": { + "target": "com.amazonaws.timestreamwrite#StringValue256", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Data model for a batch load task.

" + } + }, + "com.amazonaws.timestreamwrite#DataModelConfiguration": { + "type": "structure", + "members": { + "DataModel": { + "target": "com.amazonaws.timestreamwrite#DataModel", + "traits": { + "smithy.api#documentation": "

" + } + }, + "DataModelS3Configuration": { + "target": "com.amazonaws.timestreamwrite#DataModelS3Configuration", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#DataModelS3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamwrite#S3BucketName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "ObjectKey": { + "target": "com.amazonaws.timestreamwrite#S3ObjectKey", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#DataSourceConfiguration": { + "type": "structure", + "members": { + "DataSourceS3Configuration": { + "target": "com.amazonaws.timestreamwrite#DataSourceS3Configuration", + "traits": { + "smithy.api#documentation": "

Configuration of an S3 location for a file which contains data to load.

", + "smithy.api#required": {} + } + }, + "CsvConfiguration": { + "target": "com.amazonaws.timestreamwrite#CsvConfiguration" + }, + "DataFormat": { + "target": "com.amazonaws.timestreamwrite#BatchLoadDataFormat", + "traits": { + "smithy.api#documentation": "

This is currently CSV.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Defines configuration details about the data source.

" + } + }, + "com.amazonaws.timestreamwrite#DataSourceS3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamwrite#S3BucketName", + "traits": { + "smithy.api#documentation": "

The bucket name of the customer S3 bucket.

", + "smithy.api#required": {} + } + }, + "ObjectKeyPrefix": { + "target": "com.amazonaws.timestreamwrite#S3ObjectKey", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

\n

" + } + }, + "com.amazonaws.timestreamwrite#Database": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name that uniquely identifies this database.

" + } + }, + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

" + } + }, + "TableCount": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The total number of tables found within a Timestream database.

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The identifier of the KMS key used to encrypt the data stored in the\n database.

" + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the database was created, calculated from the Unix epoch time.

" + } + }, + "LastUpdatedTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The last time that this database was updated.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A top-level container for a table. Databases and tables are the fundamental management\n concepts in Amazon Timestream. All tables in a database are encrypted with the\n same KMS key.

" + } + }, + "com.amazonaws.timestreamwrite#DatabaseList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Database" + } + }, + "com.amazonaws.timestreamwrite#Date": { + "type": "timestamp" + }, + "com.amazonaws.timestreamwrite#DeleteDatabase": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DeleteDatabaseRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Deletes a given Timestream database. This is an irreversible\n operation. After a database is deleted, the time-series data from its tables cannot be\n recovered.\n

\n \n

All tables in the database must be deleted first, or a ValidationException error will\n be thrown.

\n

Due to the nature of distributed retries, the operation can return either success or\n a ResourceNotFoundException. Clients should consider them equivalent.

\n
\n

See code sample\n for details.

" + } + }, + "com.amazonaws.timestreamwrite#DeleteDatabaseRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database to be deleted.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DeleteTable": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DeleteTableRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Deletes a given Timestream table. This is an irreversible operation. After a\n Timestream database table is deleted, the time-series data stored in the table\n cannot be recovered.

\n \n

Due to the nature of distributed retries, the operation can return either success or\n a ResourceNotFoundException. Clients should consider them equivalent.

\n
\n

See code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#DeleteTableRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the database where the Timestream database is to be deleted.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table to be deleted.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeBatchLoadTask": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DescribeBatchLoadTaskRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#DescribeBatchLoadTaskResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Returns information about the batch load task, including configurations, mappings,\n progress, and other details. Service quotas apply. See\n code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#DescribeBatchLoadTaskRequest": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeBatchLoadTaskResponse": { + "type": "structure", + "members": { + "BatchLoadTaskDescription": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskDescription", + "traits": { + "smithy.api#documentation": "

Description of the batch load task.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeDatabase": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DescribeDatabaseRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#DescribeDatabaseResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Returns information about the database, including the database name, time that the\n database was created, and the total number of tables found within the database. Service\n quotas apply. See code sample\n for details.

" + } + }, + "com.amazonaws.timestreamwrite#DescribeDatabaseRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeDatabaseResponse": { + "type": "structure", + "members": { + "Database": { + "target": "com.amazonaws.timestreamwrite#Database", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeEndpoints": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DescribeEndpointsRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#DescribeEndpointsResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Returns a list of available endpoints to make Timestream API calls against.\n This API operation is available through both the Write and Query APIs.

\n

Because the Timestream SDKs are designed to transparently work with the\n service’s architecture, including the management and mapping of the service endpoints,\n we don't recommend that you use this API operation unless:

\n \n

For detailed information on how and when to use and implement DescribeEndpoints, see\n The\n Endpoint Discovery Pattern.

" + } + }, + "com.amazonaws.timestreamwrite#DescribeEndpointsRequest": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeEndpointsResponse": { + "type": "structure", + "members": { + "Endpoints": { + "target": "com.amazonaws.timestreamwrite#Endpoints", + "traits": { + "smithy.api#documentation": "

An Endpoints object is returned when a DescribeEndpoints\n request is made.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeTable": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DescribeTableRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#DescribeTableResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Returns information about the table, including the table name, database name, retention\n duration of the memory store and the magnetic store. Service quotas apply. See\n code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#DescribeTableRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeTableResponse": { + "type": "structure", + "members": { + "Table": { + "target": "com.amazonaws.timestreamwrite#Table", + "traits": { + "smithy.api#documentation": "

The Timestream table.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#Dimension": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

Dimension represents the metadata attributes of the time series. For example, the name\n and Availability Zone of an EC2 instance or the name of the manufacturer of a wind turbine\n are dimensions.

\n

For constraints on dimension names, see Naming\n Constraints.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamwrite#SchemaValue", + "traits": { + "smithy.api#documentation": "

The value of the dimension.

", + "smithy.api#required": {} + } + }, + "DimensionValueType": { + "target": "com.amazonaws.timestreamwrite#DimensionValueType", + "traits": { + "smithy.api#documentation": "

The data type of the dimension for the time-series data point.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the metadata attributes of the time series. For example, the name and\n Availability Zone of an EC2 instance or the name of the manufacturer of a wind turbine are\n dimensions.

" + } + }, + "com.amazonaws.timestreamwrite#DimensionMapping": { + "type": "structure", + "members": { + "SourceColumn": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "DestinationColumn": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#DimensionMappings": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#DimensionMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#DimensionValueType": { + "type": "enum", + "members": { + "VARCHAR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VARCHAR" + } + } + } + }, + "com.amazonaws.timestreamwrite#Dimensions": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Dimension" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 128 + } + } + }, + "com.amazonaws.timestreamwrite#Endpoint": { + "type": "structure", + "members": { + "Address": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

An endpoint address.

", + "smithy.api#required": {} + } + }, + "CachePeriodInMinutes": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The TTL for the endpoint, in minutes.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an available endpoint against which to make API calls against, as well as the\n TTL for that endpoint.

" + } + }, + "com.amazonaws.timestreamwrite#Endpoints": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Endpoint" + } + }, + "com.amazonaws.timestreamwrite#ErrorMessage": { + "type": "string" + }, + "com.amazonaws.timestreamwrite#Integer": { + "type": "integer", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamwrite#InternalServerException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

\n Timestream was unable to fully process this request because of an internal server\n error.

", + "smithy.api#error": "server", + "smithy.api#httpError": 500 + } + }, + "com.amazonaws.timestreamwrite#InvalidEndpointException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The requested endpoint was not valid.

", + "smithy.api#error": "client", + "smithy.api#httpError": 421 + } + }, + "com.amazonaws.timestreamwrite#ListBatchLoadTasks": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ListBatchLoadTasksRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ListBatchLoadTasksResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Provides a list of batch load tasks, along with the name, status, when the task is\n resumable until, and other details. See code\n sample for details.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamwrite#ListBatchLoadTasksRequest": { + "type": "structure", + "members": { + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

A token to specify where to start paginating. This is the NextToken from a previously\n truncated response.

" + } + }, + "MaxResults": { + "target": "com.amazonaws.timestreamwrite#PageLimit", + "traits": { + "smithy.api#documentation": "

The total number of items to return in the output. If the total number of items\n available is more than the value specified, a NextToken is provided in the output. To\n resume pagination, provide the NextToken value as argument of a subsequent API\n invocation.

" + } + }, + "TaskStatus": { + "target": "com.amazonaws.timestreamwrite#BatchLoadStatus", + "traits": { + "smithy.api#documentation": "

Status of the batch load task.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ListBatchLoadTasksResponse": { + "type": "structure", + "members": { + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

A token to specify where to start paginating. Provide the next\n ListBatchLoadTasksRequest.

" + } + }, + "BatchLoadTasks": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskList", + "traits": { + "smithy.api#documentation": "

A list of batch load task details.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#ListDatabases": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ListDatabasesRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ListDatabasesResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Returns a list of your Timestream databases. Service quotas apply. See\n code sample for\n details.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamwrite#ListDatabasesRequest": { + "type": "structure", + "members": { + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The pagination token. To resume pagination, provide the NextToken value as argument of a\n subsequent API invocation.

" + } + }, + "MaxResults": { + "target": "com.amazonaws.timestreamwrite#PaginationLimit", + "traits": { + "smithy.api#documentation": "

The total number of items to return in the output. If the total number of items\n available is more than the value specified, a NextToken is provided in the output. To\n resume pagination, provide the NextToken value as argument of a subsequent API\n invocation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ListDatabasesResponse": { + "type": "structure", + "members": { + "Databases": { + "target": "com.amazonaws.timestreamwrite#DatabaseList", + "traits": { + "smithy.api#documentation": "

A list of database names.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The pagination token. This parameter is returned when the response is truncated.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#ListTables": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ListTablesRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ListTablesResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Provides a list of tables, along with the name, status, and retention properties of each\n table. See code sample\n for details.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamwrite#ListTablesRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The pagination token. To resume pagination, provide the NextToken value as argument of a\n subsequent API invocation.

" + } + }, + "MaxResults": { + "target": "com.amazonaws.timestreamwrite#PaginationLimit", + "traits": { + "smithy.api#documentation": "

The total number of items to return in the output. If the total number of items\n available is more than the value specified, a NextToken is provided in the output. To\n resume pagination, provide the NextToken value as argument of a subsequent API\n invocation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ListTablesResponse": { + "type": "structure", + "members": { + "Tables": { + "target": "com.amazonaws.timestreamwrite#TableList", + "traits": { + "smithy.api#documentation": "

A list of tables.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

A token to specify where to start paginating. This is the NextToken from a previously\n truncated response.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#ListTagsForResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ListTagsForResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ListTagsForResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Lists all tags on a Timestream resource.

" + } + }, + "com.amazonaws.timestreamwrite#ListTagsForResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamwrite#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Timestream resource with tags to be listed. This value is an Amazon\n Resource Name (ARN).

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ListTagsForResourceResponse": { + "type": "structure", + "members": { + "Tags": { + "target": "com.amazonaws.timestreamwrite#TagList", + "traits": { + "smithy.api#documentation": "

The tags currently associated with the Timestream resource.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#Long": { + "type": "long", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamwrite#MagneticStoreRejectedDataLocation": { + "type": "structure", + "members": { + "S3Configuration": { + "target": "com.amazonaws.timestreamwrite#S3Configuration", + "traits": { + "smithy.api#documentation": "

Configuration of an S3 location to write error reports for records rejected,\n asynchronously, during magnetic store writes.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The location to write error reports for records rejected, asynchronously, during\n magnetic store writes.

" + } + }, + "com.amazonaws.timestreamwrite#MagneticStoreRetentionPeriodInDays": { + "type": "long", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 1, + "max": 73000 + } + } + }, + "com.amazonaws.timestreamwrite#MagneticStoreWriteProperties": { + "type": "structure", + "members": { + "EnableMagneticStoreWrites": { + "target": "com.amazonaws.timestreamwrite#Boolean", + "traits": { + "smithy.api#documentation": "

A flag to enable magnetic store writes.

", + "smithy.api#required": {} + } + }, + "MagneticStoreRejectedDataLocation": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreRejectedDataLocation", + "traits": { + "smithy.api#documentation": "

The location to write error reports for records rejected asynchronously during magnetic\n store writes.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The set of properties on a table for configuring magnetic store writes.

" + } + }, + "com.amazonaws.timestreamwrite#MeasureValue": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

The name of the MeasureValue.

\n

For constraints on MeasureValue names, see Naming Constraints in the Amazon Timestream Developer Guide.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The value for the MeasureValue. For information, see Data\n types.

", + "smithy.api#required": {} + } + }, + "Type": { + "target": "com.amazonaws.timestreamwrite#MeasureValueType", + "traits": { + "smithy.api#documentation": "

Contains the data type of the MeasureValue for the time-series data point.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the data attribute of the time series. For example, the CPU utilization of\n an EC2 instance or the RPM of a wind turbine are measures. MeasureValue has both name and\n value.

\n

MeasureValue is only allowed for type MULTI. Using MULTI\n type, you can pass multiple data attributes associated with the same time series in a\n single record

" + } + }, + "com.amazonaws.timestreamwrite#MeasureValueType": { + "type": "enum", + "members": { + "DOUBLE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DOUBLE" + } + }, + "BIGINT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BIGINT" + } + }, + "VARCHAR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VARCHAR" + } + }, + "BOOLEAN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BOOLEAN" + } + }, + "TIMESTAMP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "TIMESTAMP" + } + }, + "MULTI": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MULTI" + } + } + } + }, + "com.amazonaws.timestreamwrite#MeasureValues": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#MeasureValue" + } + }, + "com.amazonaws.timestreamwrite#MemoryStoreRetentionPeriodInHours": { + "type": "long", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 1, + "max": 8766 + } + } + }, + "com.amazonaws.timestreamwrite#MixedMeasureMapping": { + "type": "structure", + "members": { + "MeasureName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "SourceColumn": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "TargetMeasureName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamwrite#MeasureValueType", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#required": {} + } + }, + "MultiMeasureAttributeMappings": { + "target": "com.amazonaws.timestreamwrite#MultiMeasureAttributeMappingList", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#MixedMeasureMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#MixedMeasureMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#MultiMeasureAttributeMapping": { + "type": "structure", + "members": { + "SourceColumn": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#required": {} + } + }, + "TargetMultiMeasureAttributeName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamwrite#ScalarMeasureValueType", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#MultiMeasureAttributeMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#MultiMeasureAttributeMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#MultiMeasureMappings": { + "type": "structure", + "members": { + "TargetMultiMeasureName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "MultiMeasureAttributeMappings": { + "target": "com.amazonaws.timestreamwrite#MultiMeasureAttributeMappingList", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#PageLimit": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 100 + } + } + }, + "com.amazonaws.timestreamwrite#PaginationLimit": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 20 + } + } + }, + "com.amazonaws.timestreamwrite#PartitionKey": { + "type": "structure", + "members": { + "Type": { + "target": "com.amazonaws.timestreamwrite#PartitionKeyType", + "traits": { + "smithy.api#documentation": "

The type of the partition key. Options are DIMENSION (dimension key) and MEASURE\n (measure key).

", + "smithy.api#required": {} + } + }, + "Name": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

The name of the attribute used for a dimension key.

" + } + }, + "EnforcementInRecord": { + "target": "com.amazonaws.timestreamwrite#PartitionKeyEnforcementLevel", + "traits": { + "smithy.api#documentation": "

The level of enforcement for the specification of a dimension key in ingested records.\n Options are REQUIRED (dimension key must be specified) and OPTIONAL (dimension key does not\n have to be specified).

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An attribute used in partitioning data in a table. A dimension key partitions data\n using the values of the dimension specified by the dimension-name as partition key, while a\n measure key partitions data using measure names (values of the 'measure_name' column).\n

" + } + }, + "com.amazonaws.timestreamwrite#PartitionKeyEnforcementLevel": { + "type": "enum", + "members": { + "REQUIRED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REQUIRED" + } + }, + "OPTIONAL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "OPTIONAL" + } + } + } + }, + "com.amazonaws.timestreamwrite#PartitionKeyList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#PartitionKey" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#PartitionKeyType": { + "type": "enum", + "members": { + "DIMENSION": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DIMENSION" + } + }, + "MEASURE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MEASURE" + } + } + } + }, + "com.amazonaws.timestreamwrite#Record": { + "type": "structure", + "members": { + "Dimensions": { + "target": "com.amazonaws.timestreamwrite#Dimensions", + "traits": { + "smithy.api#documentation": "

Contains the list of dimensions for time-series data points.

" + } + }, + "MeasureName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

Measure represents the data attribute of the time series. For example, the CPU\n utilization of an EC2 instance or the RPM of a wind turbine are measures.

" + } + }, + "MeasureValue": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

Contains the measure value for the time-series data point.

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamwrite#MeasureValueType", + "traits": { + "smithy.api#documentation": "

Contains the data type of the measure value for the time-series data point. Default\n type is DOUBLE. For more information, see Data\n types.

" + } + }, + "Time": { + "target": "com.amazonaws.timestreamwrite#StringValue256", + "traits": { + "smithy.api#documentation": "

Contains the time at which the measure value for the data point was collected. The time\n value plus the unit provides the time elapsed since the epoch. For example, if the time\n value is 12345 and the unit is ms, then 12345 ms\n have elapsed since the epoch.

" + } + }, + "TimeUnit": { + "target": "com.amazonaws.timestreamwrite#TimeUnit", + "traits": { + "smithy.api#documentation": "

The granularity of the timestamp unit. It indicates if the time value is in seconds,\n milliseconds, nanoseconds, or other supported values. Default is MILLISECONDS.\n

" + } + }, + "Version": { + "target": "com.amazonaws.timestreamwrite#RecordVersion", + "traits": { + "smithy.api#default": null, + "smithy.api#documentation": "

64-bit attribute used for record updates. Write requests for duplicate data with a\n higher version number will update the existing measure value and version. In cases where\n the measure value is the same, Version will still be updated. Default value is\n 1.

\n \n

\n Version must be 1 or greater, or you will receive a\n ValidationException error.

\n
" + } + }, + "MeasureValues": { + "target": "com.amazonaws.timestreamwrite#MeasureValues", + "traits": { + "smithy.api#documentation": "

Contains the list of MeasureValue for time-series data points.

\n

This is only allowed for type MULTI. For scalar values, use\n MeasureValue attribute of the record directly.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a time-series data point being written into Timestream. Each record\n contains an array of dimensions. Dimensions represent the metadata attributes of a\n time-series data point, such as the instance name or Availability Zone of an EC2 instance.\n A record also contains the measure name, which is the name of the measure being collected\n (for example, the CPU utilization of an EC2 instance). Additionally, a record contains the\n measure value and the value type, which is the data type of the measure value. Also, the\n record contains the timestamp of when the measure was collected and the timestamp unit,\n which represents the granularity of the timestamp.

\n

Records have a Version field, which is a 64-bit long that you\n can use for updating data points. Writes of a duplicate record with the same dimension,\n timestamp, and measure name but different measure value will only succeed if the\n Version attribute of the record in the write request is higher than that of\n the existing record. Timestream defaults to a Version of\n 1 for records without the Version field.

" + } + }, + "com.amazonaws.timestreamwrite#RecordIndex": { + "type": "integer", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamwrite#RecordVersion": { + "type": "long", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamwrite#Records": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Record" + }, + "traits": { + "smithy.api#length": { + "min": 1, + "max": 100 + } + } + }, + "com.amazonaws.timestreamwrite#RecordsIngested": { + "type": "structure", + "members": { + "Total": { + "target": "com.amazonaws.timestreamwrite#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Total count of successfully ingested records.

" + } + }, + "MemoryStore": { + "target": "com.amazonaws.timestreamwrite#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Count of records ingested into the memory store.

" + } + }, + "MagneticStore": { + "target": "com.amazonaws.timestreamwrite#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Count of records ingested into the magnetic store.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Information on the records ingested by this request.

" + } + }, + "com.amazonaws.timestreamwrite#RejectedRecord": { + "type": "structure", + "members": { + "RecordIndex": { + "target": "com.amazonaws.timestreamwrite#RecordIndex", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The index of the record in the input request for WriteRecords. Indexes begin with 0.\n

" + } + }, + "Reason": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#documentation": "

The reason why a record was not successfully inserted into Timestream.\n Possible causes of failure include:

\n
    \n
  • \n

    Records with duplicate data where there are multiple records with the same\n dimensions, timestamps, and measure names but:

    \n
      \n
    • \n

      Measure values are different

      \n
    • \n
    • \n

      Version is not present in the request, or the value of\n version in the new record is equal to or lower than the existing value

      \n
    • \n
    \n

    If Timestream rejects data for this case, the\n ExistingVersion field in the RejectedRecords response\n will indicate the current record’s version. To force an update, you can resend the\n request with a version for the record set to a value greater than the\n ExistingVersion.

    \n
  • \n
  • \n

    Records with timestamps that lie outside the retention duration of the memory\n store.

    \n \n

    When the retention window is updated, you will receive a\n RejectedRecords exception if you immediately try to ingest data\n within the new window. To avoid a RejectedRecords exception, wait\n until the duration of the new window to ingest new data. For further information,\n see Best\n Practices for Configuring Timestream and the\n explanation of how storage works in Timestream.

    \n
    \n
  • \n
  • \n

    Records with dimensions or measures that exceed the Timestream defined\n limits.

    \n
  • \n
\n

For more information, see Access Management in the\n Timestream Developer Guide.

" + } + }, + "ExistingVersion": { + "target": "com.amazonaws.timestreamwrite#RecordVersion", + "traits": { + "smithy.api#default": null, + "smithy.api#documentation": "

The existing version of the record. This value is populated in scenarios where an\n identical record exists with a higher version than the version in the write request.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents records that were not successfully inserted into Timestream due to\n data validation issues that must be resolved before reinserting time-series data into the\n system.

" + } + }, + "com.amazonaws.timestreamwrite#RejectedRecords": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#RejectedRecord" + } + }, + "com.amazonaws.timestreamwrite#RejectedRecordsException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage" + }, + "RejectedRecords": { + "target": "com.amazonaws.timestreamwrite#RejectedRecords", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

WriteRecords would throw this exception in the following cases:

\n
    \n
  • \n

    Records with duplicate data where there are multiple records with the same\n dimensions, timestamps, and measure names but:

    \n
      \n
    • \n

      Measure values are different

      \n
    • \n
    • \n

      Version is not present in the request or the value of\n version in the new record is equal to or lower than the existing value

      \n
    • \n
    \n

    In this case, if Timestream rejects data, the\n ExistingVersion field in the RejectedRecords response\n will indicate the current record’s version. To force an update, you can resend the\n request with a version for the record set to a value greater than the\n ExistingVersion.

    \n
  • \n
  • \n

    Records with timestamps that lie outside the retention duration of the memory\n store.

    \n
  • \n
  • \n

    Records with dimensions or measures that exceed the Timestream defined\n limits.

    \n
  • \n
\n

For more information, see Quotas in the Amazon Timestream Developer Guide.

", + "smithy.api#error": "client", + "smithy.api#httpError": 419 + } + }, + "com.amazonaws.timestreamwrite#ReportConfiguration": { + "type": "structure", + "members": { + "ReportS3Configuration": { + "target": "com.amazonaws.timestreamwrite#ReportS3Configuration", + "traits": { + "smithy.api#documentation": "

Configuration of an S3 location to write error reports and events for a batch\n load.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Report configuration for a batch load task. This contains details about where error\n reports are stored.

" + } + }, + "com.amazonaws.timestreamwrite#ReportS3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamwrite#S3BucketName", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#required": {} + } + }, + "ObjectKeyPrefix": { + "target": "com.amazonaws.timestreamwrite#S3ObjectKeyPrefix", + "traits": { + "smithy.api#documentation": "

" + } + }, + "EncryptionOption": { + "target": "com.amazonaws.timestreamwrite#S3EncryptionOption", + "traits": { + "smithy.api#documentation": "

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#ResourceCreateAPIName": { + "type": "string", + "traits": { + "smithy.api#pattern": "^[a-zA-Z0-9_.-]+$" + } + }, + "com.amazonaws.timestreamwrite#ResourceName": { + "type": "string" + }, + "com.amazonaws.timestreamwrite#ResourceNotFoundException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The operation tried to access a nonexistent resource. The resource might not be\n specified correctly, or its status might not be ACTIVE.

", + "smithy.api#error": "client", + "smithy.api#httpError": 404 + } + }, + "com.amazonaws.timestreamwrite#ResumeBatchLoadTask": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ResumeBatchLoadTaskRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ResumeBatchLoadTaskResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

\n

" + } + }, + "com.amazonaws.timestreamwrite#ResumeBatchLoadTaskRequest": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task to resume.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ResumeBatchLoadTaskResponse": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#RetentionProperties": { + "type": "structure", + "members": { + "MemoryStoreRetentionPeriodInHours": { + "target": "com.amazonaws.timestreamwrite#MemoryStoreRetentionPeriodInHours", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The duration for which data must be stored in the memory store.

", + "smithy.api#required": {} + } + }, + "MagneticStoreRetentionPeriodInDays": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreRetentionPeriodInDays", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The duration for which data must be stored in the magnetic store.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Retention properties contain the duration for which your time-series data must be stored\n in the magnetic store and the memory store.

" + } + }, + "com.amazonaws.timestreamwrite#S3BucketName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 3, + "max": 63 + }, + "smithy.api#pattern": "^[a-z0-9][\\.\\-a-z0-9]{1,61}[a-z0-9]$" + } + }, + "com.amazonaws.timestreamwrite#S3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamwrite#S3BucketName", + "traits": { + "smithy.api#documentation": "

The bucket name of the customer S3 bucket.

" + } + }, + "ObjectKeyPrefix": { + "target": "com.amazonaws.timestreamwrite#S3ObjectKeyPrefix", + "traits": { + "smithy.api#documentation": "

The object key preview for the customer S3 location.

" + } + }, + "EncryptionOption": { + "target": "com.amazonaws.timestreamwrite#S3EncryptionOption", + "traits": { + "smithy.api#documentation": "

The encryption option for the customer S3 location. Options are S3 server-side\n encryption with an S3 managed key or Amazon Web Services managed key.

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The KMS key ID for the customer S3 location when encrypting with an\n Amazon Web Services managed key.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The configuration that specifies an S3 location.

" + } + }, + "com.amazonaws.timestreamwrite#S3EncryptionOption": { + "type": "enum", + "members": { + "SSE_S3": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SSE_S3" + } + }, + "SSE_KMS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SSE_KMS" + } + } + } + }, + "com.amazonaws.timestreamwrite#S3ObjectKey": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 1024 + }, + "smithy.api#pattern": "^[a-zA-Z0-9|!\\-_*'\\(\\)]([a-zA-Z0-9]|[!\\-_*'\\(\\)\\/.])+$" + } + }, + "com.amazonaws.timestreamwrite#S3ObjectKeyPrefix": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 928 + }, + "smithy.api#pattern": "^[a-zA-Z0-9|!\\-_*'\\(\\)]([a-zA-Z0-9]|[!\\-_*'\\(\\)\\/.])+$" + } + }, + "com.amazonaws.timestreamwrite#ScalarMeasureValueType": { + "type": "enum", + "members": { + "DOUBLE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DOUBLE" + } + }, + "BIGINT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BIGINT" + } + }, + "BOOLEAN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BOOLEAN" + } + }, + "VARCHAR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VARCHAR" + } + }, + "TIMESTAMP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "TIMESTAMP" + } + } + } + }, + "com.amazonaws.timestreamwrite#Schema": { + "type": "structure", + "members": { + "CompositePartitionKey": { + "target": "com.amazonaws.timestreamwrite#PartitionKeyList", + "traits": { + "smithy.api#documentation": "

A non-empty list of partition keys defining the attributes used to partition the table\n data. The order of the list determines the partition hierarchy. The name and type of each\n partition key as well as the partition key order cannot be changed after the table is\n created. However, the enforcement level of each partition key can be changed.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A Schema specifies the expected data model of the table.

" + } + }, + "com.amazonaws.timestreamwrite#SchemaName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#SchemaValue": { + "type": "string" + }, + "com.amazonaws.timestreamwrite#ServiceQuotaExceededException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The instance quota of resource exceeded for this account.

", + "smithy.api#error": "client", + "smithy.api#httpError": 402 + } + }, + "com.amazonaws.timestreamwrite#String": { + "type": "string" + }, + "com.amazonaws.timestreamwrite#StringValue1": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 1 + } + } + }, + "com.amazonaws.timestreamwrite#StringValue2048": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + } + } + }, + "com.amazonaws.timestreamwrite#StringValue256": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 256 + } + } + }, + "com.amazonaws.timestreamwrite#Table": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name that uniquely identifies this table.

" + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

" + } + }, + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database that contains this table.

" + } + }, + "TableStatus": { + "target": "com.amazonaws.timestreamwrite#TableStatus", + "traits": { + "smithy.api#documentation": "

The current state of the table:

\n
    \n
  • \n

    \n DELETING - The table is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The table is ready for use.

    \n
  • \n
" + } + }, + "RetentionProperties": { + "target": "com.amazonaws.timestreamwrite#RetentionProperties", + "traits": { + "smithy.api#documentation": "

The retention duration for the memory store and magnetic store.

" + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream table was created.

" + } + }, + "LastUpdatedTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream table was last updated.

" + } + }, + "MagneticStoreWriteProperties": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreWriteProperties", + "traits": { + "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" + } + }, + "Schema": { + "target": "com.amazonaws.timestreamwrite#Schema", + "traits": { + "smithy.api#documentation": "

The schema of the table.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a database table in Timestream. Tables contain one or more related\n time series. You can modify the retention duration of the memory store and the magnetic\n store for a table.

" + } + }, + "com.amazonaws.timestreamwrite#TableList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Table" + } + }, + "com.amazonaws.timestreamwrite#TableStatus": { + "type": "enum", + "members": { + "ACTIVE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ACTIVE" + } + }, + "DELETING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DELETING" + } + }, + "RESTORING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESTORING" + } + } + } + }, + "com.amazonaws.timestreamwrite#Tag": { + "type": "structure", + "members": { + "Key": { + "target": "com.amazonaws.timestreamwrite#TagKey", + "traits": { + "smithy.api#documentation": "

The key of the tag. Tag keys are case sensitive.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamwrite#TagValue", + "traits": { + "smithy.api#documentation": "

The value of the tag. Tag values are case-sensitive and can be null.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A tag is a label that you assign to a Timestream database and/or table. Each\n tag consists of a key and an optional value, both of which you define. With tags, you can\n categorize databases and/or tables, for example, by purpose, owner, or environment.

" + } + }, + "com.amazonaws.timestreamwrite#TagKey": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 128 + } + } + }, + "com.amazonaws.timestreamwrite#TagKeyList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#TagKey" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.timestreamwrite#TagList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Tag" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.timestreamwrite#TagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#TagResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#TagResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Associates a set of tags with a Timestream resource. You can then activate\n these user-defined tags so that they appear on the Billing and Cost Management console for\n cost allocation tracking.

" + } + }, + "com.amazonaws.timestreamwrite#TagResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamwrite#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

Identifies the Timestream resource to which tags should be added. This value\n is an Amazon Resource Name (ARN).

", + "smithy.api#required": {} + } + }, + "Tags": { + "target": "com.amazonaws.timestreamwrite#TagList", + "traits": { + "smithy.api#documentation": "

The tags to be assigned to the Timestream resource.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#TagResourceResponse": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#TagValue": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 0, + "max": 256 + } + } + }, + "com.amazonaws.timestreamwrite#ThrottlingException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Too many requests were made by a user and they exceeded the service quotas. The request\n was throttled.

", + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.timestreamwrite#TimeUnit": { + "type": "enum", + "members": { + "MILLISECONDS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MILLISECONDS" + } + }, + "SECONDS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SECONDS" + } + }, + "MICROSECONDS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MICROSECONDS" + } + }, + "NANOSECONDS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NANOSECONDS" + } + } + } + }, + "com.amazonaws.timestreamwrite#Timestream_20181101": { + "type": "service", + "version": "2018-11-01", + "operations": [ + { + "target": "com.amazonaws.timestreamwrite#CreateBatchLoadTask" + }, + { + "target": "com.amazonaws.timestreamwrite#CreateDatabase" + }, + { + "target": "com.amazonaws.timestreamwrite#CreateTable" + }, + { + "target": "com.amazonaws.timestreamwrite#DeleteDatabase" + }, + { + "target": "com.amazonaws.timestreamwrite#DeleteTable" + }, + { + "target": "com.amazonaws.timestreamwrite#DescribeBatchLoadTask" + }, + { + "target": "com.amazonaws.timestreamwrite#DescribeDatabase" + }, + { + "target": "com.amazonaws.timestreamwrite#DescribeEndpoints" + }, + { + "target": "com.amazonaws.timestreamwrite#DescribeTable" + }, + { + "target": "com.amazonaws.timestreamwrite#ListBatchLoadTasks" + }, + { + "target": "com.amazonaws.timestreamwrite#ListDatabases" + }, + { + "target": "com.amazonaws.timestreamwrite#ListTables" + }, + { + "target": "com.amazonaws.timestreamwrite#ListTagsForResource" + }, + { + "target": "com.amazonaws.timestreamwrite#ResumeBatchLoadTask" + }, + { + "target": "com.amazonaws.timestreamwrite#TagResource" + }, + { + "target": "com.amazonaws.timestreamwrite#UntagResource" + }, + { + "target": "com.amazonaws.timestreamwrite#UpdateDatabase" + }, + { + "target": "com.amazonaws.timestreamwrite#UpdateTable" + }, + { + "target": "com.amazonaws.timestreamwrite#WriteRecords" + } + ], + "traits": { + "aws.api#clientEndpointDiscovery": { + "operation": "com.amazonaws.timestreamwrite#DescribeEndpoints", + "error": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + "aws.api#service": { + "sdkId": "Timestream Write", + "arnNamespace": "timestream", + "cloudFormationName": "TimestreamWrite", + "cloudTrailEventSource": "timestreamwrite.amazonaws.com", + "endpointPrefix": "ingest.timestream" + }, + "aws.auth#sigv4": { + "name": "timestream" + }, + "aws.protocols#awsJson1_0": {}, + "smithy.api#documentation": "Amazon Timestream Write\n

Amazon Timestream is a fast, scalable, fully managed time-series database service\n that makes it easy to store and analyze trillions of time-series data points per day. With\n Timestream, you can easily store and analyze IoT sensor data to derive insights\n from your IoT applications. You can analyze industrial telemetry to streamline equipment\n management and maintenance. You can also store and analyze log data and metrics to improve\n the performance and availability of your applications.

\n

Timestream is built from the ground up to effectively ingest, process, and\n store time-series data. It organizes data to optimize query processing. It automatically\n scales based on the volume of data ingested and on the query volume to ensure you receive\n optimal performance while inserting and querying data. As your data grows over time,\n Timestream’s adaptive query processing engine spans across storage tiers to\n provide fast analysis while reducing costs.

", + "smithy.api#title": "Amazon Timestream Write", + "smithy.rules#endpointRuleSet": { + "version": "1.0", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": false, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "Boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "Boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Region" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://ingest.timestream-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://ingest.timestream-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://ingest.timestream.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://ingest.timestream.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" + } + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips enabled and dualstack disabled", + "expect": { + "error": "Invalid Configuration: FIPS and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack enabled", + "expect": { + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } + } + ], + "version": "1.0" + } + } + }, + "com.amazonaws.timestreamwrite#UntagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#UntagResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#UntagResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Removes the association of tags from a Timestream resource.

" + } + }, + "com.amazonaws.timestreamwrite#UntagResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamwrite#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Timestream resource that the tags will be removed from. This value is an\n Amazon Resource Name (ARN).

", + "smithy.api#required": {} + } + }, + "TagKeys": { + "target": "com.amazonaws.timestreamwrite#TagKeyList", + "traits": { + "smithy.api#documentation": "

A list of tags keys. Existing tags of the resource whose keys are members of this list\n will be removed from the Timestream resource.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#UntagResourceResponse": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#UpdateDatabase": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#UpdateDatabaseRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#UpdateDatabaseResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Modifies the KMS key for an existing database. While updating the\n database, you must specify the database name and the identifier of the new KMS key to be used (KmsKeyId). If there are any concurrent\n UpdateDatabase requests, first writer wins.

\n

See code sample\n for details.

" + } + }, + "com.amazonaws.timestreamwrite#UpdateDatabaseRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the database.

", + "smithy.api#required": {} + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The identifier of the new KMS key (KmsKeyId) to be used to\n encrypt the data stored in the database. If the KmsKeyId currently registered\n with the database is the same as the KmsKeyId in the request, there will not\n be any update.

\n

You can specify the KmsKeyId using any of the following:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN:\n arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN:\n arn:aws:kms:us-east-1:111122223333:alias/ExampleAlias\n

    \n
  • \n
", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#UpdateDatabaseResponse": { + "type": "structure", + "members": { + "Database": { + "target": "com.amazonaws.timestreamwrite#Database" + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#UpdateTable": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#UpdateTableRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#UpdateTableResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Modifies the retention duration of the memory store and magnetic store for your Timestream table. Note that the change in retention duration takes effect immediately.\n For example, if the retention period of the memory store was initially set to 2 hours and\n then changed to 24 hours, the memory store will be capable of holding 24 hours of data, but\n will be populated with 24 hours of data 22 hours after this change was made. Timestream does not retrieve data from the magnetic store to populate the memory store.

\n

See code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#UpdateTableRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

", + "smithy.api#required": {} + } + }, + "RetentionProperties": { + "target": "com.amazonaws.timestreamwrite#RetentionProperties", + "traits": { + "smithy.api#documentation": "

The retention duration of the memory store and the magnetic store.

" + } + }, + "MagneticStoreWriteProperties": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreWriteProperties", + "traits": { + "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" + } + }, + "Schema": { + "target": "com.amazonaws.timestreamwrite#Schema", + "traits": { + "smithy.api#documentation": "

The schema of the table.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#UpdateTableResponse": { + "type": "structure", + "members": { + "Table": { + "target": "com.amazonaws.timestreamwrite#Table", + "traits": { + "smithy.api#documentation": "

The updated Timestream table.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#ValidationException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

An invalid or malformed request.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.timestreamwrite#WriteRecords": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#WriteRecordsRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#WriteRecordsResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#RejectedRecordsException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Enables you to write your time-series data into Timestream. You can specify a\n single data point or a batch of data points to be inserted into the system. Timestream offers you a flexible schema that auto detects the column names and data\n types for your Timestream tables based on the dimension names and data types of\n the data points you specify when invoking writes into the database.

\n

Timestream supports eventual consistency read semantics. This means that when\n you query data immediately after writing a batch of data into Timestream, the\n query results might not reflect the results of a recently completed write operation. The\n results may also include some stale data. If you repeat the query request after a short\n time, the results should return the latest data. Service quotas apply.

\n

See code sample for\n details.

\n

\n Upserts\n

\n

You can use the Version parameter in a WriteRecords request to\n update data points. Timestream tracks a version number with each record.\n Version defaults to 1 when it's not specified for the record\n in the request. Timestream updates an existing record’s measure value along with\n its Version when it receives a write request with a higher\n Version number for that record. When it receives an update request where\n the measure value is the same as that of the existing record, Timestream still\n updates Version, if it is greater than the existing value of\n Version. You can update a data point as many times as desired, as long as\n the value of Version continuously increases.

\n

For example, suppose you write a new record without indicating Version in\n the request. Timestream stores this record, and set Version to\n 1. Now, suppose you try to update this record with a\n WriteRecords request of the same record with a different measure value but,\n like before, do not provide Version. In this case, Timestream will\n reject this update with a RejectedRecordsException since the updated record’s\n version is not greater than the existing value of Version.

\n

However, if you were to resend the update request with Version set to\n 2, Timestream would then succeed in updating the record’s value,\n and the Version would be set to 2. Next, suppose you sent a\n WriteRecords request with this same record and an identical measure value,\n but with Version set to 3. In this case, Timestream\n would only update Version to 3. Any further updates would need to\n send a version number greater than 3, or the update requests would receive a\n RejectedRecordsException.

" + } + }, + "com.amazonaws.timestreamwrite#WriteRecordsRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

", + "smithy.api#required": {} + } + }, + "CommonAttributes": { + "target": "com.amazonaws.timestreamwrite#Record", + "traits": { + "smithy.api#documentation": "

A record that contains the common measure, dimension, time, and version attributes\n shared across all the records in the request. The measure and dimension attributes\n specified will be merged with the measure and dimension attributes in the records object\n when the data is written into Timestream. Dimensions may not overlap, or a\n ValidationException will be thrown. In other words, a record must contain\n dimensions with unique names.

" + } + }, + "Records": { + "target": "com.amazonaws.timestreamwrite#Records", + "traits": { + "smithy.api#documentation": "

An array of records that contain the unique measure, dimension, time, and version\n attributes for each time-series data point.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#WriteRecordsResponse": { + "type": "structure", + "members": { + "RecordsIngested": { + "target": "com.amazonaws.timestreamwrite#RecordsIngested", + "traits": { + "smithy.api#documentation": "

Information on the records ingested by this request.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + } + } +} diff --git a/aws/sdk/aws-models/transcribe-streaming.json b/aws/sdk/aws-models/transcribe-streaming.json index 93e63cde42..a1464b0462 100644 --- a/aws/sdk/aws-models/transcribe-streaming.json +++ b/aws/sdk/aws-models/transcribe-streaming.json @@ -2654,8 +2654,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2667,8 +2667,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2680,8 +2680,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2693,8 +2693,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2706,8 +2706,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2719,8 +2719,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2732,8 +2732,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2745,8 +2745,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2758,8 +2758,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2771,8 +2771,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2784,8 +2784,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2797,8 +2797,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2810,8 +2810,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2823,8 +2823,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2836,8 +2836,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2849,8 +2849,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2862,8 +2862,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2875,8 +2875,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2888,8 +2888,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2901,8 +2901,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2914,8 +2914,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2927,8 +2927,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2940,8 +2940,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2953,8 +2953,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2966,8 +2966,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2979,8 +2990,30 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2992,8 +3025,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -3005,8 +3049,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3018,8 +3062,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3031,8 +3075,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3043,8 +3087,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3055,10 +3099,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.lock b/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.lock new file mode 100644 index 0000000000..7cf3c5ef2e --- /dev/null +++ b/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.lock @@ -0,0 +1,2451 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "assert-json-diff" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4259cbe96513d2f1073027a259fc2ca917feb3026a5a8d984e3628e490255cc0" +dependencies = [ + "extend", + "serde", + "serde_json", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "aws-config" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-http 0.0.0-smithy-rs-head", + "aws-sdk-sso", + "aws-sdk-sts", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-json 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "fastrand 2.0.0", + "hex", + "http", + "hyper", + "ring", + "time", + "tokio", + "tower", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "fastrand 2.0.0", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcdb2f7acbc076ff5ad05e7864bdb191ca70a6fd07668dc3a1a8bcd051de5ae" +dependencies = [ + "aws-smithy-async 0.55.3", + "aws-smithy-types 0.55.3", + "fastrand 1.9.0", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-endpoint" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-endpoint" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cce1c41a6cfaa726adee9ebb9a56fcd2bbfd8be49fd8a04c5e20fd968330b04" +dependencies = [ + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "aws-types 0.55.3", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-http" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "http-body", + "lazy_static", + "percent-encoding", + "pin-project-lite", + "tracing", +] + +[[package]] +name = "aws-http" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aadbc44e7a8f3e71c8b374e03ecd972869eb91dd2bc89ed018954a52ba84bc44" +dependencies = [ + "aws-credential-types 0.55.3", + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "aws-types 0.55.3", + "bytes", + "http", + "http-body", + "lazy_static", + "percent-encoding", + "pin-project-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-s3" +version = "0.0.0-local" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-endpoint 0.0.0-smithy-rs-head", + "aws-http 0.0.0-smithy-rs-head", + "aws-sig-auth 0.0.0-smithy-rs-head", + "aws-sigv4 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-checksums 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-eventstream 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-json 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-smithy-xml 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "http-body", + "once_cell", + "percent-encoding", + "regex", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-s3" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba197193cbb4bcb6aad8d99796b2291f36fa89562ded5d4501363055b0de89f" +dependencies = [ + "aws-credential-types 0.55.3", + "aws-endpoint 0.55.3", + "aws-http 0.55.3", + "aws-sig-auth 0.55.3", + "aws-sigv4 0.55.3", + "aws-smithy-async 0.55.3", + "aws-smithy-checksums 0.55.3", + "aws-smithy-client 0.55.3", + "aws-smithy-eventstream 0.55.3", + "aws-smithy-http 0.55.3", + "aws-smithy-http-tower 0.55.3", + "aws-smithy-json 0.55.3", + "aws-smithy-types 0.55.3", + "aws-smithy-xml 0.55.3", + "aws-types 0.55.3", + "bytes", + "http", + "http-body", + "once_cell", + "percent-encoding", + "regex", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "0.0.0-local" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-endpoint 0.0.0-smithy-rs-head", + "aws-http 0.0.0-smithy-rs-head", + "aws-sig-auth 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-json 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "0.0.0-local" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-endpoint 0.0.0-smithy-rs-head", + "aws-http 0.0.0-smithy-rs-head", + "aws-sig-auth 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-json 0.0.0-smithy-rs-head", + "aws-smithy-query", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-smithy-xml 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "regex", + "tower", + "tracing", +] + +[[package]] +name = "aws-sig-auth" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-sigv4 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-eventstream 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "http", + "tracing", +] + +[[package]] +name = "aws-sig-auth" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b94acb10af0c879ecd5c7bdf51cda6679a0a4f4643ce630905a77673bfa3c61" +dependencies = [ + "aws-credential-types 0.55.3", + "aws-sigv4 0.55.3", + "aws-smithy-eventstream 0.55.3", + "aws-smithy-http 0.55.3", + "aws-types 0.55.3", + "http", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-eventstream 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http", + "once_cell", + "percent-encoding", + "regex", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2ce6f507be68e968a33485ced670111d1cbad161ddbbab1e313c03d37d8f4c" +dependencies = [ + "aws-smithy-eventstream 0.55.3", + "aws-smithy-http 0.55.3", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http", + "once_cell", + "percent-encoding", + "regex", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-async" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bda3996044c202d75b91afeb11a9afae9db9a721c6a7a427410018e286b880" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http", + "http-body", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ed8b96d95402f3f6b8b57eb4e0e45ee365f78b1a924faf20ff6e97abf1eae6" +dependencies = [ + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http", + "http-body", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-client" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-protocol-test 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "fastrand 2.0.0", + "http", + "http-body", + "hyper", + "hyper-rustls 0.24.1", + "lazy_static", + "pin-project-lite", + "rustls 0.21.5", + "serde", + "serde_json", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-client" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a86aa6e21e86c4252ad6a0e3e74da9617295d8d6e374d552be7d3059c41cedd" +dependencies = [ + "aws-smithy-async 0.55.3", + "aws-smithy-http 0.55.3", + "aws-smithy-http-tower 0.55.3", + "aws-smithy-protocol-test 0.55.3", + "aws-smithy-types 0.55.3", + "bytes", + "fastrand 1.9.0", + "http", + "http-body", + "hyper", + "hyper-rustls 0.23.2", + "lazy_static", + "pin-project-lite", + "rustls 0.20.8", + "serde", + "serde_json", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460c8da5110835e3d9a717c61f5556b20d03c32a1dec57f8fc559b360f733bb8" +dependencies = [ + "aws-smithy-types 0.55.3", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-eventstream 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "aws-smithy-http" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b3b693869133551f135e1f2c77cb0b8277d9e3e17feaf2213f735857c4f0d28" +dependencies = [ + "aws-smithy-eventstream 0.55.3", + "aws-smithy-types 0.55.3", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae4f6c5798a247fac98a867698197d9ac22643596dc3777f0c76b91917616b9" +dependencies = [ + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-types 0.0.0-smithy-rs-head", +] + +[[package]] +name = "aws-smithy-json" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f9f42fbfa96d095194a632fbac19f60077748eba536eb0b9fecc28659807f8" +dependencies = [ + "aws-smithy-types 0.55.3", +] + +[[package]] +name = "aws-smithy-protocol-test" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "assert-json-diff", + "http", + "pretty_assertions", + "regex", + "roxmltree", + "serde_json", + "thiserror", +] + +[[package]] +name = "aws-smithy-protocol-test" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabbf8d2bfefa4870ba497c1ae3b40e5e26be18af1cb8c871856b0a393a15ffa" +dependencies = [ + "assert-json-diff", + "http", + "pretty_assertions", + "regex", + "roxmltree", + "serde_json", + "thiserror", +] + +[[package]] +name = "aws-smithy-query" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-types 0.0.0-smithy-rs-head", + "urlencoding", +] + +[[package]] +name = "aws-smithy-types" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "base64-simd", + "itoa", + "num-integer", + "ryu", + "serde", + "time", +] + +[[package]] +name = "aws-smithy-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a3d0bf4f324f4ef9793b86a1701d9700fbcdbd12a846da45eed104c634c6e8" +dependencies = [ + "base64-simd", + "itoa", + "num-integer", + "ryu", + "time", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b9d12875731bd07e767be7baad95700c3137b56730ec9ddeedb52a5e5ca63b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "http", + "rustc_version", + "tracing", +] + +[[package]] +name = "aws-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd209616cc8d7bfb82f87811a5c655dc97537f592689b18743bddf5dc5c4829" +dependencies = [ + "aws-credential-types 0.55.3", + "aws-smithy-async 0.55.3", + "aws-smithy-client 0.55.3", + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "http", + "rustc_version", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bytes-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "bitflags", + "clap_lex", + "indexmap", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "futures", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "extend" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47da3a72ec598d9c8937a7ebca8962a5c7a1f28444e38c2b33c771ba3f55f05" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "h2" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls 0.20.8", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.23.4", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls 0.21.5", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "orchestrator-vs-middleware" +version = "0.1.0" +dependencies = [ + "aws-config", + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-sdk-s3 0.0.0-local", + "aws-sdk-s3 0.28.0", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-client 0.55.3", + "criterion", + "http", + "tokio", +] + +[[package]] +name = "os_str_bytes" +version = "6.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "serde" +version = "1.0.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "serde_json" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "time" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +dependencies = [ + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.8", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.5", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.27", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "xmlparser" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.toml b/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.toml new file mode 100644 index 0000000000..e4b6af798a --- /dev/null +++ b/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "orchestrator-vs-middleware" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aws-config = { path = "../../build/aws-sdk/sdk/aws-config" } +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } +aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3" } +aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "wiremock"] } +criterion = { version = "0.4", features = ["async_tokio"] } +http = "0.2.3" +middleware-s3 = { version = "0.28", package = "aws-sdk-s3", features = ["test-util"] } +middleware-smithy-client = { version = "0.55.3", package = "aws-smithy-client", features = ["test-util", "rustls"] } +tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } + +[profile.release] +debug = 1 + +[[bench]] +name = "middleware_vs_orchestrator" +harness = false diff --git a/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/README.md b/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/README.md new file mode 100644 index 0000000000..0f2e81f432 --- /dev/null +++ b/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/README.md @@ -0,0 +1,6 @@ +### Middleware vs. Orchestrator Benchmark + +To run the benchmark: +```bash +./gradlew :aws:sdk:assemble && (cd aws/sdk/integration-tests/s3 && cargo bench) +``` diff --git a/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/middleware_vs_orchestrator.rs b/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/middleware_vs_orchestrator.rs new file mode 100644 index 0000000000..6920f80fe0 --- /dev/null +++ b/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/middleware_vs_orchestrator.rs @@ -0,0 +1,101 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[macro_use] +extern crate criterion; +use aws_sdk_s3 as s3; +use criterion::{BenchmarkId, Criterion}; + +macro_rules! test_connection { + ($package:ident) => { + $package::test_connection::infallible_connection_fn(|req| { + assert_eq!( + "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + req.uri().to_string() + ); + assert!(req.headers().contains_key("authorization")); + http::Response::builder() + .status(200) + .body( + r#" + + test-bucket + prefix~ + 1 + 1000 + false + + some-file.file + 2009-10-12T17:50:30.000Z + 434234 + STANDARD + + +"#, + ) + .unwrap() + }) + }; +} + +async fn orchestrator(client: &s3::Client) { + let _output = client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .send() + .await + .expect("successful execution"); +} + +async fn middleware(client: &middleware_s3::Client) { + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .send() + .await + .expect("successful execution"); +} + +fn bench(c: &mut Criterion) { + let orchestrator_client = { + let conn = test_connection!(aws_smithy_client); + let config = aws_sdk_s3::Config::builder() + .credentials_provider(aws_sdk_s3::config::Credentials::for_tests()) + .region(aws_sdk_s3::config::Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); + aws_sdk_s3::Client::from_conf(config) + }; + let middleware_client = { + let conn = test_connection!(middleware_smithy_client); + let config = middleware_s3::Config::builder() + .credentials_provider(middleware_s3::config::Credentials::for_tests()) + .region(middleware_s3::config::Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); + middleware_s3::Client::from_conf(config) + }; + + let mut group = c.benchmark_group("compare"); + let param = "S3 ListObjectsV2"; + group.bench_with_input( + BenchmarkId::new("middleware (last_release)", param), + param, + |b, _| { + b.to_async(tokio::runtime::Runtime::new().unwrap()) + .iter(|| async { middleware(&middleware_client).await }) + }, + ); + group.bench_with_input(BenchmarkId::new("orchestrator", param), param, |b, _| { + b.to_async(tokio::runtime::Runtime::new().unwrap()) + .iter(|| async { orchestrator(&orchestrator_client).await }) + }); + group.finish(); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/aws/sdk/benchmarks/s3-throughput/README.md b/aws/sdk/benchmarks/s3-throughput/README.md new file mode 100644 index 0000000000..6bbceaebe4 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/README.md @@ -0,0 +1,32 @@ +S3 Benchmark +============ + +This directory contains a S3 benchmark that measures throughput when using the AWS Rust SDK to put and get objects to/from S3. +The `benchmark/` directory has the Rust benchmark code, and `infrastructure/` contains the CDK infrastructure to stand up +a `c5n.18xlarge` EC2 instance, compile/run the benchmark, and upload the results to S3. + +Example of running the `get-object-multipart` benchmark in local dev: + +```bash +cargo run -- --bench get-object-multipart --fs disk --part-size-bytes 5242880 --size-bytes 6000000 --bucket my-test-bucket --region us-west-2 --profile my-aws-credentials-profile +``` + +On Linux, the `--fs` option can be given either `disk` or `tmpfs` (for an in-memory filesystem), while on other OSes, only `disk` is available. + +In addition to `get-object-multipart`, there are `put-object-multipart`, `put-object`, and `get-object`. All of these take the +same CLI arguments, although `--part-size-bytes` is unused by `put-object` and `get-object`. + +To run the actual benchmark, it must be deployed via CDK from the `infrastructure/` directory: + +```bash +npm install +npm run build +npx cdk bootstrap --profile my-aws-credentials-profile +npx cdk synthesize --profile my-aws-credentials-profile +npx cdk deploy --profile my-aws-credentials-profile +``` + +The `lib/instrastructure-stack.ts` defines the actual CloudFormation stack that creates the EC2 Instance. +This instance is configured to run the `assets/init_instance.sh` and `assets/run_benchmark.sh` scripts on start-up. +It's also configured for SSH access via a key pair named "S3BenchmarkKeyPair". This key pair has to be created manually +before deploying the CDK stack. diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.lock b/aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.lock new file mode 100644 index 0000000000..a435022a58 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.lock @@ -0,0 +1,1818 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "async-trait" +version = "0.1.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79fa67157abdfd688a259b6648808757db9347af834624f27ec646da976aee5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "aws-config" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdcf0d683fe9c23d32cf5b53c9918ea0a500375a9fb20109802552658e576c9" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-sdk-sso", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http", + "hyper", + "ring", + "time", + "tokio", + "tower", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcdb2f7acbc076ff5ad05e7864bdb191ca70a6fd07668dc3a1a8bcd051de5ae" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "fastrand", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-endpoint" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cce1c41a6cfaa726adee9ebb9a56fcd2bbfd8be49fd8a04c5e20fd968330b04" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "aws-types", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-http" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aadbc44e7a8f3e71c8b374e03ecd972869eb91dd2bc89ed018954a52ba84bc44" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "http-body", + "lazy_static", + "percent-encoding", + "pin-project-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-s3" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba197193cbb4bcb6aad8d99796b2291f36fa89562ded5d4501363055b0de89f" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-client", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "http", + "http-body", + "once_cell", + "percent-encoding", + "regex", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8b812340d86d4a766b2ca73f740dfd47a97c2dff0c06c8517a16d88241957e4" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265fac131fbfc188e5c3d96652ea90ecc676a934e3174eaaee523c6cec040b3b" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "http", + "regex", + "tower", + "tracing", +] + +[[package]] +name = "aws-sig-auth" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b94acb10af0c879ecd5c7bdf51cda6679a0a4f4643ce630905a77673bfa3c61" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-types", + "http", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2ce6f507be68e968a33485ced670111d1cbad161ddbbab1e313c03d37d8f4c" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-http", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http", + "once_cell", + "percent-encoding", + "regex", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bda3996044c202d75b91afeb11a9afae9db9a721c6a7a427410018e286b880" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ed8b96d95402f3f6b8b57eb4e0e45ee365f78b1a924faf20ff6e97abf1eae6" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http", + "http-body", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-client" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a86aa6e21e86c4252ad6a0e3e74da9617295d8d6e374d552be7d3059c41cedd" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-types", + "bytes", + "fastrand", + "http", + "http-body", + "hyper", + "hyper-rustls", + "lazy_static", + "pin-project-lite", + "rustls", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460c8da5110835e3d9a717c61f5556b20d03c32a1dec57f8fc559b360f733bb8" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b3b693869133551f135e1f2c77cb0b8277d9e3e17feaf2213f735857c4f0d28" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae4f6c5798a247fac98a867698197d9ac22643596dc3777f0c76b91917616b9" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f9f42fbfa96d095194a632fbac19f60077748eba536eb0b9fecc28659807f8" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98819eb0b04020a1c791903533b638534ae6c12e2aceda3e6e6fba015608d51d" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a3d0bf4f324f4ef9793b86a1701d9700fbcdbd12a846da45eed104c634c6e8" +dependencies = [ + "base64-simd", + "itoa", + "num-integer", + "ryu", + "time", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b9d12875731bd07e767be7baad95700c3137b56730ec9ddeedb52a5e5ca63b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd209616cc8d7bfb82f87811a5c655dc97537f592689b18743bddf5dc5c4829" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-types", + "http", + "rustc_version", + "tracing", +] + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "benchmark" +version = "0.1.0" +dependencies = [ + "async-trait", + "aws-config", + "aws-sdk-s3", + "aws-smithy-client", + "aws-smithy-http", + "clap", + "hyper", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bytes-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401a4694d2bf92537b6867d94de48c4842089645fdcdf6c71865b175d836e9c2" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" +dependencies = [ + "anstyle", + "bitflags", + "clap_lex", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfea2db42e9927a3845fb268a10a72faed6d416065f77873f05e411457c363e" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +dependencies = [ + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "xmlparser" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.toml b/aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.toml new file mode 100644 index 0000000000..5ac77a1f81 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "benchmark" +version = "0.1.0" +authors = ["AWS Rust SDK Team ", "John DiSanti "] +description = "S3 benchmark" +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" +publish = false + +[dependencies] +aws-config = "0.55.3" +aws-sdk-s3 = "0.28.0" +aws-smithy-http = "0.55.3" +aws-smithy-client= { version = "0.55.3", features = ["client-hyper"] } +clap = { version = "4.3.2", default-features = false, features = ["derive", "std", "help"] } +tokio = { version = "1.28.2", features = ["full"] } +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +tracing = "0.1" +async-trait = "0.1.68" +hyper = { version = "0.14.27", features = ["client"] } diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/src/get_test.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/get_test.rs new file mode 100644 index 0000000000..a9588255a6 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/src/get_test.rs @@ -0,0 +1,49 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::{verify, Args, BoxError}; +use async_trait::async_trait; +use aws_config::SdkConfig; +use aws_sdk_s3::Client; +use std::path::{Path, PathBuf}; + +pub(crate) struct GetTestResult { + pub(crate) expected: PathBuf, + pub(crate) actual: PathBuf, +} + +#[async_trait] +pub(crate) trait GetBenchmark { + type Setup: Send; + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup; + async fn do_get( + &self, + state: Self::Setup, + target_path: &Path, + args: &Args, + ) -> Result; + async fn do_bench( + &self, + state: Self::Setup, + args: &Args, + expected_path: &Path, + ) -> Result { + let target_path = expected_path.with_extension("downloaded"); + let downloaded_path = self.do_get(state, &target_path, args).await?; + Ok(GetTestResult { + expected: expected_path.to_path_buf(), + actual: downloaded_path, + }) + } + + async fn verify( + &self, + _client: &Client, + _args: &Args, + result: GetTestResult, + ) -> Result<(), BoxError> { + verify::diff(&result.actual, &result.expected).await + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/src/latencies.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/latencies.rs new file mode 100644 index 0000000000..50b54a273d --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/src/latencies.rs @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::fmt; +use std::time; + +const ONE_GIGABYTE: u64 = 1000 * 1000 * 1000; + +#[derive(Debug)] +pub struct Latencies { + object_size_bytes: u64, + raw_values: Vec, +} + +impl Latencies { + pub fn new(object_size_bytes: u64) -> Self { + Self { + object_size_bytes, + raw_values: Vec::new(), + } + } + + pub fn push(&mut self, value: time::Duration) { + self.raw_values.push(value.as_secs_f64()); + } + + /// Calculates the standard deviation squared of the given values. + fn variance(values: &[f64], average: f64) -> f64 { + values + .iter() + .map(|value| (value - average).powi(2)) + .sum::() + / values.len() as f64 + } +} + +impl fmt::Display for Latencies { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let object_size_gigabits = self.object_size_bytes as f64 / ONE_GIGABYTE as f64 * 8f64; + + let average_latency = self.raw_values.iter().sum::() / self.raw_values.len() as f64; + let lowest_latency = self + .raw_values + .iter() + .fold(std::f64::INFINITY, |acc, &x| acc.min(x)); + let variance = Self::variance(&self.raw_values, average_latency); + writeln!(f, "Latency values (s): {:?}", self.raw_values)?; + writeln!(f, "Average latency (s): {average_latency}")?; + writeln!(f, "Latency variance (s): {variance}")?; + writeln!(f, "Object size (Gigabits): {object_size_gigabits}")?; + writeln!( + f, + "Average throughput (Gbps): {}", + object_size_gigabits / average_latency + )?; + writeln!( + f, + "Highest average throughput (Gbps): {}", + object_size_gigabits / lowest_latency + )?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::latencies::{Latencies, ONE_GIGABYTE}; + + #[test] + fn test_display() { + let latencies = Latencies { + object_size_bytes: 30 * ONE_GIGABYTE, + raw_values: vec![ + 33.261f64, 41.114, 33.014, 32.97, 34.138, 33.972, 33.001, 34.12, + ], + }; + + let expected = "\ + Latency values (s): [33.261, 41.114, 33.014, 32.97, 34.138, 33.972, 33.001, 34.12]\n\ + Average latency (s): 34.448750000000004\n\ + Latency variance (s): 6.576178687499994\n\ + Object size (Gigabits): 240\n\ + Average throughput (Gbps): 6.966871076599295\n\ + Highest average throughput (Gbps): 7.279344858962694\n"; + let actual = latencies.to_string(); + assert_eq!(expected, actual); + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/src/main.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/main.rs new file mode 100644 index 0000000000..ad9e6aefb1 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/src/main.rs @@ -0,0 +1,305 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::latencies::Latencies; +use crate::multipart_put::{put_object_multipart, PutObjectMultipart}; +use async_trait::async_trait; +use aws_config::SdkConfig; +use aws_sdk_s3 as s3; +use aws_sdk_s3::Client; +use clap::Parser as _; +use s3::error::DisplayErrorContext; +use s3::primitives::ByteStream; +use std::error::Error as StdError; +use std::path::Path; +use std::path::PathBuf; +use std::process; +use std::process::{Command, Stdio}; +use std::time; + +mod get_test; +mod latencies; +mod multipart_get; +mod multipart_put; +mod put_test; +mod verify; + +pub type BoxError = Box; + +pub const BENCH_KEY: &str = "s3_bench_file"; + +use crate::get_test::GetBenchmark; +use crate::put_test::PutBenchmark; +use tracing::Instrument; + +#[derive(Copy, Clone, Debug, clap::ValueEnum)] +pub enum Fs { + #[cfg(target_os = "linux")] + // Use tmpfs + Tmpfs, + // Use the disk + Disk, +} + +#[derive(Copy, Clone, Debug, clap::ValueEnum)] +pub enum Bench { + PutObject, + GetObject, + PutObjectMultipart, + GetObjectMultipart, +} + +#[derive(Debug, Clone, clap::Parser)] +#[command()] +pub struct Args { + /// Which benchmark to run. + #[arg(long)] + bench: Bench, + + /// Local FS type to use. + #[arg(long)] + fs: Fs, + + /// Size of the object to benchmark with. + #[arg(long)] + size_bytes: u64, + + /// S3 bucket to test against. + #[arg(long)] + bucket: String, + + /// AWS region to use. Defaults to us-east-1. + #[arg(long, default_value = "us-east-1")] + region: String, + + /// AWS credentials profile to use. + #[arg(long)] + profile: Option, + + /// Part size for multipart benchmarks. Defaults to 8 MiB. + #[arg(long, default_value_t = 8_388_608)] + part_size_bytes: u64, + + /// Number of benchmark iterations to perform. + #[arg(long, default_value_t = 8)] + iterations: usize, + + /// Number of concurrent uploads/downloads to perform. + #[arg(long, default_value_t = 4)] + concurrency: usize, + + #[arg(long, default_value_t = 1000)] + part_upload_timeout_millis: u64, +} + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt::init(); + + let args = Args::parse(); + let config = { + let mut loader = + aws_config::from_env().region(s3::config::Region::new(args.region.clone())); + if let Some(profile) = args.profile.as_ref() { + loader = loader.profile_name(profile); + } + loader.load().await + }; + + let result = match args.bench { + Bench::PutObject => benchmark_put_object(&config, &args).await, + Bench::GetObject => benchmark_get_object(&config, &args).await, + Bench::PutObjectMultipart => benchmark_put_object_multipart(&config, &args).await, + Bench::GetObjectMultipart => benchmark_get_object_multipart(&config, &args).await, + }; + match result { + Ok(latencies) => { + println!("benchmark succeeded"); + println!("=============== {:?} Result ================", args.bench); + println!("{latencies}"); + println!("=========================================================="); + } + Err(err) => { + println!("benchmark failed: {}", DisplayErrorContext(err.as_ref())); + process::exit(1); + } + } +} + +macro_rules! benchmark { + ($sdk_config:ident, $args:ident, setup => $setup:expr, operation => $operation:expr) => {{ + #[allow(unused)] + use crate::get_test::GetBenchmark; + #[allow(unused)] + use crate::put_test::PutBenchmark; + println!("setting up..."); + let test_file_path = generate_test_file($args)?; + let setup_client = aws_sdk_s3::Client::new(&$sdk_config); + $setup(&setup_client, $args, &test_file_path).await?; + println!("setup complete"); + + let mut latencies = Latencies::new($args.size_bytes); + for i in 0..$args.iterations { + let span = tracing::info_span!("run operation"); + let bench = $operation; + let client = bench.prepare($sdk_config).await; + let start = time::Instant::now(); + let result = bench + .do_bench(client, $args, &test_file_path) + .instrument(span) + .await?; + let latency = start.elapsed(); + if let Err(e) = bench.verify(&setup_client, $args, result).await { + println!("benchmark did not finish correctly: {}", e); + } + latencies.push(latency); + println!( + "finished iteration {i} in {} seconds", + latency.as_secs_f64() + ); + } + + Ok(latencies) + }}; +} + +async fn benchmark_put_object(conf: &SdkConfig, args: &Args) -> Result { + struct PutObject; + #[async_trait] + impl PutBenchmark for PutObject { + type Setup = Client; + + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup { + Client::new(conf) + } + + async fn do_put( + &self, + state: Self::Setup, + target_key: &str, + local_file: &Path, + args: &Args, + ) -> Result<(), BoxError> { + state + .put_object() + .bucket(&args.bucket) + .key(target_key) + .body(ByteStream::from_path(local_file).await?) + .send() + .await?; + Ok(()) + } + } + benchmark!(conf, args, setup => no_setup, operation => PutObject) +} + +async fn benchmark_get_object(client: &SdkConfig, args: &Args) -> Result { + struct GetObject; + #[async_trait] + impl GetBenchmark for GetObject { + type Setup = Client; + + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup { + Client::new(&conf) + } + + async fn do_get( + &self, + state: Self::Setup, + target_path: &Path, + args: &Args, + ) -> Result { + let output = state + .get_object() + .bucket(&args.bucket) + .key(BENCH_KEY) + .send() + .await?; + let mut body = output.body.into_async_read(); + let mut file = tokio::fs::File::create(target_path).await?; + tokio::io::copy(&mut body, &mut file).await?; + Ok(target_path.to_path_buf()) + } + } + benchmark!(client, args, setup => put_object_intelligent, operation => GetObject) +} + +async fn benchmark_put_object_multipart( + conf: &SdkConfig, + args: &Args, +) -> Result { + benchmark!(conf, args, setup => no_setup, operation => PutObjectMultipart) +} + +async fn benchmark_get_object_multipart( + config: &SdkConfig, + args: &Args, +) -> Result { + benchmark!(config, args, setup => put_object_intelligent, operation => multipart_get::GetObjectMultipart::new()) +} + +fn generate_test_file(args: &Args) -> Result { + let path = match args.fs { + Fs::Disk => format!("/tmp/{BENCH_KEY}").into(), + #[cfg(target_os = "linux")] + Fs::Tmpfs => { + if !PathBuf::from("/dev/shm").exists() { + return Err("tmpfs not available on this machine".into()); + } + format!("/dev/shm/{BENCH_KEY}").into() + } + }; + + let mut yes_process = Command::new("yes") + .arg("01234567890abcdefghijklmnopqrstuvwxyz") + .stdout(Stdio::piped()) + .spawn()?; + + let mut head_process = Command::new("head") + .arg("-c") + .arg(format!("{}", args.size_bytes)) + .stdin(yes_process.stdout.take().unwrap()) + .stdout(Stdio::piped()) + .spawn()?; + + let mut file = std::fs::File::create(&path)?; + head_process.stdout.as_mut().unwrap(); + std::io::copy(&mut head_process.stdout.take().unwrap(), &mut file)?; + + let exit_status = head_process.wait()?; + + if !exit_status.success() { + Err("failed to generate temp file")? + } + + Ok(path) +} + +async fn no_setup(_client: &s3::Client, _args: &Args, _path: &Path) -> Result<(), BoxError> { + Ok(()) +} + +async fn put_object_intelligent( + client: &s3::Client, + args: &Args, + path: &Path, +) -> Result<(), BoxError> { + if args.size_bytes > args.part_size_bytes { + put_object_multipart(&[client.clone()], args, BENCH_KEY, path).await + } else { + put_object(client, args, path).await + } +} + +async fn put_object(client: &s3::Client, args: &Args, path: &Path) -> Result<(), BoxError> { + client + .put_object() + .bucket(&args.bucket) + .key(BENCH_KEY) + .body(ByteStream::from_path(&path).await?) + .send() + .await?; + Ok(()) +} diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_get.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_get.rs new file mode 100644 index 0000000000..ab8832816c --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_get.rs @@ -0,0 +1,183 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::{Args, BoxError, BENCH_KEY}; +use async_trait::async_trait; +use aws_config::SdkConfig; +use aws_sdk_s3 as s3; +use aws_sdk_s3::Client; +use aws_smithy_http::byte_stream::AggregatedBytes; +use std::fmt; +use std::fs::File; +use std::os::unix::fs::FileExt; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::{Duration, SystemTime}; + +use crate::get_test::GetBenchmark; +use tokio::sync::Semaphore; +use tokio::task::spawn_blocking; +use tokio::time::timeout; +use tracing::{info_span, Instrument}; + +pub(crate) struct GetObjectMultipart {} +impl GetObjectMultipart { + pub(crate) fn new() -> Self { + Self {} + } +} + +#[async_trait] +impl GetBenchmark for GetObjectMultipart { + type Setup = Vec; + + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup { + let clients = (0..32).map(|_| Client::new(&conf)).collect::>(); + for client in &clients { + let _ = client.list_buckets().send().await; + } + clients + } + + async fn do_get( + &self, + state: Self::Setup, + target_path: &Path, + args: &Args, + ) -> Result { + get_object_multipart(&state, args, target_path, &args.bucket, BENCH_KEY).await?; + Ok(target_path.to_path_buf()) + } +} + +pub async fn get_object_multipart( + clients: &[s3::Client], + args: &Args, + target_path: &Path, + bucket: &str, + key: &str, +) -> Result<(), BoxError> { + let mut part_count = (args.size_bytes / args.part_size_bytes + 1) as u64; + let mut size_of_last_part = (args.size_bytes % args.part_size_bytes) as u64; + if size_of_last_part == 0 { + size_of_last_part = args.part_size_bytes as u64; + part_count -= 1; + } + + let ranges = (0..part_count).map(|i| { + if i == part_count - 1 { + let start = i * args.part_size_bytes as u64; + ContentRange::new(start, start + size_of_last_part - 1) + } else { + ContentRange::new( + i * args.part_size_bytes as u64, + (i + 1) * args.part_size_bytes as u64 - 1, + ) + } + }); + + let semaphore = Arc::new(Semaphore::new(args.concurrency)); + let mut tasks = Vec::new(); + let file = Arc::new(File::create(target_path)?); + for (id, range) in ranges.enumerate() { + let semaphore = semaphore.clone(); + let client = clients[id % clients.len()].clone(); + let file = file.clone(); + let bucket = bucket.to_string(); + let key = key.to_string(); + tasks.push(tokio::spawn( + async move { + let _permit = semaphore.acquire_owned().await?; + + let start = SystemTime::now(); + tracing::debug!(range = ?range); + + let body = + download_part_retry_on_timeout(id, &range, &client, &bucket, &key).await?; + tracing::debug!(id =? id, load_duration = ?start.elapsed().unwrap()); + let mut offset = range.start; + let write_duration = SystemTime::now(); + spawn_blocking(move || { + for part in body.into_segments() { + file.write_all_at(&part, offset)?; + offset += part.len() as u64; + } + Ok::<_, BoxError>(()) + }) + .await??; + tracing::debug!(id =? id, write_duration = ?write_duration.elapsed().unwrap()); + Result::<_, BoxError>::Ok(()) + } + .instrument(info_span!("run-collect-part", id = id)), + )); + } + for task in tasks { + task.await??; + } + + Ok(()) +} + +async fn download_part_retry_on_timeout( + id: usize, + range: &ContentRange, + client: &Client, + bucket: &str, + key: &str, +) -> Result { + loop { + match timeout( + Duration::from_millis(1000), + download_part(id, range, client, bucket, key), + ) + .await + { + Ok(result) => return result, + Err(_) => tracing::warn!("get part timeout"), + } + } +} + +async fn download_part( + id: usize, + range: &ContentRange, + client: &Client, + bucket: &str, + key: &str, +) -> Result { + let part = client + .get_object() + .bucket(bucket) + .key(key) + .range(range.to_string()) + .send() + .instrument(info_span!("get_object", id = id)) + .await?; + + let body = part + .body + .collect() + .instrument(info_span!("collect-body", id = id)) + .await?; + Ok(body) +} + +#[derive(Debug)] +struct ContentRange { + start: u64, + end: u64, +} + +impl ContentRange { + fn new(start: u64, end: u64) -> Self { + Self { start, end } + } +} + +impl fmt::Display for ContentRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "bytes={}-{}", self.start, self.end) + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_put.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_put.rs new file mode 100644 index 0000000000..942c3d8020 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_put.rs @@ -0,0 +1,172 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::put_test::PutBenchmark; +use crate::{Args, BoxError, BENCH_KEY}; +use async_trait::async_trait; +use aws_config::SdkConfig; +use aws_sdk_s3 as s3; +use aws_sdk_s3::Client; +use aws_smithy_http::byte_stream::ByteStream; +use s3::types::CompletedMultipartUpload; +use s3::types::CompletedPart; +use std::io::SeekFrom; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::{Duration, SystemTime}; +use tokio::fs::File; +use tokio::io::{AsyncReadExt, AsyncSeekExt}; +use tokio::sync::{OwnedSemaphorePermit, Semaphore}; +use tokio::time::timeout; + +pub(crate) struct PutObjectMultipart; + +#[async_trait] +impl PutBenchmark for PutObjectMultipart { + type Setup = Vec; + + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup { + let clients = (0..32).map(|_| Client::new(&conf)).collect::>(); + for client in &clients { + let _ = client.list_buckets().send().await; + } + clients + } + + async fn do_put( + &self, + state: Self::Setup, + target_key: &str, + local_file: &Path, + args: &Args, + ) -> Result<(), BoxError> { + put_object_multipart(&state, args, target_key, local_file).await + } +} + +pub async fn put_object_multipart( + client: &[s3::Client], + args: &Args, + target_key: &str, + path: &Path, +) -> Result<(), BoxError> { + let upload_id = client[0] + .create_multipart_upload() + .bucket(&args.bucket) + .key(target_key) + .send() + .await? + .upload_id + .expect("missing upload id"); + + let mut part_count = args.size_bytes / args.part_size_bytes + 1; + let mut size_of_last_part = args.size_bytes % args.part_size_bytes; + if size_of_last_part == 0 { + size_of_last_part = args.part_size_bytes; + part_count -= 1; + } + + let semaphore = Arc::new(Semaphore::new(args.concurrency)); + let mut tasks = Vec::new(); + for part in 0..part_count { + let offset = args.part_size_bytes * part; + let length = if part == part_count - 1 { + size_of_last_part + } else { + args.part_size_bytes + }; + let permit = semaphore.clone().acquire_owned().await?; + tasks.push(tokio::spawn(upload_part_retry_on_timeout( + permit, + client[part as usize % client.len()].clone(), + args.bucket.clone(), + upload_id.clone(), + path.to_path_buf(), + offset, + length, + part, + Duration::from_millis(args.part_upload_timeout_millis), + ))); + } + let mut parts = Vec::new(); + for task in tasks { + parts.push(task.await??); + } + + client[0] + .complete_multipart_upload() + .bucket(&args.bucket) + .key(BENCH_KEY) + .upload_id(&upload_id) + .multipart_upload( + CompletedMultipartUpload::builder() + .set_parts(Some(parts)) + .build(), + ) + .send() + .await?; + + Ok(()) +} + +async fn upload_part_retry_on_timeout( + permit: OwnedSemaphorePermit, + client: s3::Client, + bucket: String, + upload_id: String, + path: PathBuf, + offset: u64, + length: u64, + part: u64, + timeout_dur: Duration, +) -> Result { + loop { + match timeout( + timeout_dur, + upload_part(&client, &bucket, &upload_id, &path, offset, length, part), + ) + .await + { + Ok(res) => { + drop(permit); + return res; + } + Err(_) => tracing::warn!(id = ?part, "timeout!"), + } + } +} + +#[allow(clippy::too_many_arguments)] +async fn upload_part( + client: &s3::Client, + bucket: &str, + upload_id: &str, + path: &Path, + offset: u64, + length: u64, + part: u64, +) -> Result { + let start = SystemTime::now(); + let mut file = File::open(path).await?; + file.seek(SeekFrom::Start(offset)).await?; + let mut buf = vec![0; length as usize]; + file.read_exact(&mut buf).await?; + let stream = ByteStream::from(buf); + let part_output = client + .upload_part() + .key(BENCH_KEY) + .bucket(bucket) + .upload_id(upload_id) + .body(stream) + .part_number(part as i32 + 1) // S3 takes a 1-based index + .send() + .await?; + tracing::debug!(part = ?part, upload_duration = ?start.elapsed().unwrap(), "upload-part"); + Ok(CompletedPart::builder() + .part_number(part as i32 + 1) + .e_tag(part_output.e_tag.expect("must have an e-tag")) + .build()) +} diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/src/put_test.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/put_test.rs new file mode 100644 index 0000000000..606778a216 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/src/put_test.rs @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::multipart_get::get_object_multipart; +use crate::{verify, Args, BoxError, BENCH_KEY}; +use async_trait::async_trait; +use aws_config::SdkConfig; +use aws_sdk_s3::Client; +use std::env::temp_dir; +use std::path::{Path, PathBuf}; + +pub(crate) struct PutTestResult { + local_file: PathBuf, + bucket: String, + key: String, +} + +#[async_trait] +pub(crate) trait PutBenchmark { + type Setup: Send; + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup; + async fn do_put( + &self, + state: Self::Setup, + target_key: &str, + local_file: &Path, + args: &Args, + ) -> Result<(), BoxError>; + async fn do_bench( + &self, + state: Self::Setup, + args: &Args, + file: &Path, + ) -> Result { + self.do_put(state, BENCH_KEY, file, args).await?; + Ok(PutTestResult { + local_file: file.to_path_buf(), + bucket: args.bucket.clone(), + key: BENCH_KEY.to_string(), + }) + } + + async fn verify( + &self, + client: &Client, + args: &Args, + result: PutTestResult, + ) -> Result<(), BoxError> { + let dir = temp_dir(); + let downloaded_path = dir.join("downloaded_file"); + get_object_multipart( + &[client.clone()][..], + args, + &downloaded_path, + &result.bucket, + &result.key, + ) + .await?; + verify::diff(&result.local_file, &downloaded_path).await + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/benchmark/src/verify.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/verify.rs new file mode 100644 index 0000000000..6b6c50904f --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/benchmark/src/verify.rs @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::BoxError; +use std::path::Path; +use std::time::SystemTime; +use tokio::process::Command; + +pub(crate) async fn diff(a: &Path, b: &Path) -> Result<(), BoxError> { + let start_diff = SystemTime::now(); + let diff_ok = Command::new("diff") + .arg(a) + .arg(b) + .arg("-q") + .spawn() + .unwrap() + .wait() + .await + .unwrap(); + tracing::info!(diff_duration = ?start_diff.elapsed().unwrap()); + if !diff_ok.success() { + Err("files differ")? + } else { + Ok(()) + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/.eslintrc.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/.eslintrc.json new file mode 100644 index 0000000000..2612e1f904 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/.eslintrc.json @@ -0,0 +1,16 @@ +{ + "env": { + "browser": false, + "es2021": true + }, + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 13, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "@typescript-eslint/no-empty-function": "off" + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/.gitignore b/aws/sdk/benchmarks/s3-throughput/infrastructure/.gitignore new file mode 100644 index 0000000000..3fae389cc5 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/.gitignore @@ -0,0 +1,9 @@ +*.js +!jest.config.js +*.d.ts +/node_modules +/build + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/.npmignore b/aws/sdk/benchmarks/s3-throughput/infrastructure/.npmignore new file mode 100644 index 0000000000..c1d6d45dcf --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/.prettierrc b/aws/sdk/benchmarks/s3-throughput/infrastructure/.prettierrc new file mode 100644 index 0000000000..6505068233 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/.prettierrc @@ -0,0 +1,5 @@ +tabWidth: 4 +singleQuote: false +bracketSpacing: true +trailingComma: all +printWidth: 100 diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/assets/init_instance.sh b/aws/sdk/benchmarks/s3-throughput/infrastructure/assets/init_instance.sh new file mode 100755 index 0000000000..ba2c9ff446 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/assets/init_instance.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +echo "init_instance.sh starting" +set -eux + +BENCHMARK_ZIP_PATH="${1}" +echo "benchmark zip path: ${BENCHMARK_ZIP_PATH}" + +sudo yum -y install \ + autoconf \ + automake \ + gcc \ + gcc-c++ \ + git \ + make \ + openssl-devel + +# Install Rustup and Rust +curl https://static.rust-lang.org/rustup/archive/1.24.3/x86_64-unknown-linux-gnu/rustup-init --output rustup-init +echo "3dc5ef50861ee18657f9db2eeb7392f9c2a6c95c90ab41e45ab4ca71476b4338 rustup-init" | sha256sum --check +chmod +x rustup-init +./rustup-init -y --no-modify-path --profile minimal --default-toolchain 1.67.1 +rm rustup-init + +# Verify install +source "${HOME}/.cargo/env" +rustc --version +cargo --version + +# Compile the benchmark +unzip -d benchmark "${BENCHMARK_ZIP_PATH}" +cd benchmark +cargo build --release diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/assets/run_benchmark.sh b/aws/sdk/benchmarks/s3-throughput/infrastructure/assets/run_benchmark.sh new file mode 100755 index 0000000000..56c617d3e4 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/assets/run_benchmark.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +echo "run_benchmark.sh starting" +set -eux + +BENCHMARK_BUCKET="${1}" +echo "benchmark bucket: ${BENCHMARK_BUCKET}" + +COMMON_ARGS="--concurrency 30 --bucket ${BENCHMARK_BUCKET} --region us-east-1" + +source "${HOME}/.cargo/env" +cd benchmark + +# 1B +for fs in "tmpfs" "disk"; do + BENCH_RESULT_FILE="bench_results_put_object_${fs}_1B.txt" + cargo run --release -- --bench put-object --fs "${fs}" --size-bytes 1 ${COMMON_ARGS} &> "${BENCH_RESULT_FILE}" + aws s3 cp "${BENCH_RESULT_FILE}" "s3://${BENCHMARK_BUCKET}/" + + BENCH_RESULT_FILE="bench_results_get_object_${fs}_1B.txt" + cargo run --release -- --bench get-object --fs "${fs}" --size-bytes 1 ${COMMON_ARGS} &> "${BENCH_RESULT_FILE}" + aws s3 cp "${BENCH_RESULT_FILE}" "s3://${BENCHMARK_BUCKET}/" +done + +# multipart +for fs in "tmpfs" "disk"; do + for size_bytes in "8388607" "8388609" "134217728" "4294967296" "32212254720"; do + BENCH_RESULT_FILE="bench_results_put_object_multipart_${fs}_${size_bytes}B.txt" + cargo run --release -- --bench put-object-multipart --fs "${fs}" --size-bytes "${size_bytes}" --part-size-bytes "8388608" ${COMMON_ARGS} &> "${BENCH_RESULT_FILE}" + aws s3 cp "${BENCH_RESULT_FILE}" "s3://${BENCHMARK_BUCKET}/" + + BENCH_RESULT_FILE="bench_results_get_object_multipart_${fs}_${size_bytes}B.txt" + cargo run --release -- --bench get-object-multipart --fs "${fs}" --size-bytes "${size_bytes}" --part-size-bytes "8388608" ${COMMON_ARGS} &> "${BENCH_RESULT_FILE}" + aws s3 cp "${BENCH_RESULT_FILE}" "s3://${BENCHMARK_BUCKET}/" + done +done + +echo "Benchmark finished. Results have been uploaded to ${BENCHMARK_BUCKET}" diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/bin/infrastructure.ts b/aws/sdk/benchmarks/s3-throughput/infrastructure/bin/infrastructure.ts new file mode 100644 index 0000000000..6370d93bb6 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/bin/infrastructure.ts @@ -0,0 +1,14 @@ +#!/usr/bin/env node +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import "source-map-support/register"; +import * as cdk from "aws-cdk-lib"; +import { InfrastructureStack } from "../lib/infrastructure-stack"; + +const app = new cdk.App(); +new InfrastructureStack(app, "InfrastructureStack", { + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: "us-east-1" }, +}); diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.context.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.context.json new file mode 100644 index 0000000000..f64f1bac1d --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.context.json @@ -0,0 +1,10 @@ +{ + "availability-zones:account=422563069514:region=us-east-1": [ + "us-east-1a", + "us-east-1b", + "us-east-1c", + "us-east-1d", + "us-east-1e", + "us-east-1f" + ] +} diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.json new file mode 100644 index 0000000000..9f5670963c --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.json @@ -0,0 +1,53 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/infrastructure.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/lib/infrastructure-stack.ts b/aws/sdk/benchmarks/s3-throughput/infrastructure/lib/infrastructure-stack.ts new file mode 100644 index 0000000000..416111dbbd --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/lib/infrastructure-stack.ts @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as cdk from "aws-cdk-lib"; +import { Construct } from "constructs"; +import * as ec2 from "aws-cdk-lib/aws-ec2"; +import * as s3 from "aws-cdk-lib/aws-s3"; +import * as iam from "aws-cdk-lib/aws-iam"; +import * as assets from "aws-cdk-lib/aws-s3-assets"; +import * as path from "path"; + +export class InfrastructureStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const assetInitInstance = new assets.Asset(this, "assetInitInstance", { + path: path.join(__dirname, "../assets/init_instance.sh"), + }); + const assetRunBenchmark = new assets.Asset(this, "assetRunBenchmark", { + path: path.join(__dirname, "../assets/run_benchmark.sh"), + }); + const assetBenchmark = new assets.Asset(this, "assetBenchmark", { + path: path.join(__dirname, "../../benchmark"), + }); + const assetBucket = s3.Bucket.fromBucketName( + this, + "assetBucket", + assetInitInstance.s3BucketName, + ); + + const benchmarkBucket = new s3.Bucket(this, "benchmarkBucket", { + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + encryption: s3.BucketEncryption.S3_MANAGED, + enforceSSL: true, + versioned: false, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + const instanceUserData = ec2.UserData.forLinux(); + const initInstancePath = instanceUserData.addS3DownloadCommand({ + bucket: assetBucket, + bucketKey: assetInitInstance.s3ObjectKey, + }); + const runBenchmarkPath = instanceUserData.addS3DownloadCommand({ + bucket: assetBucket, + bucketKey: assetRunBenchmark.s3ObjectKey, + }); + const benchmarkPath = instanceUserData.addS3DownloadCommand({ + bucket: assetBucket, + bucketKey: assetBenchmark.s3ObjectKey, + }); + instanceUserData.addExecuteFileCommand({ + filePath: initInstancePath, + arguments: `${benchmarkPath}`, + }); + instanceUserData.addExecuteFileCommand({ + filePath: runBenchmarkPath, + arguments: `${benchmarkBucket.bucketName}`, + }); + + const vpc = new ec2.Vpc(this, "VPC", {}); + const securityGroup = new ec2.SecurityGroup(this, "securityGroup", { + vpc, + description: "Allow outbound and SSH inbound", + allowAllOutbound: true, + }); + securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), "SSH"); + + const executionRole = new iam.Role(this, "executionRole", { + assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"), + }); + assetBucket.grantRead(executionRole); + benchmarkBucket.grantReadWrite(executionRole); + + new ec2.Instance(this, `S3Benchmark_${this.stackName}`, { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.C5N, ec2.InstanceSize.XLARGE18), + vpc, + machineImage: ec2.MachineImage.latestAmazonLinux2023(), + userData: instanceUserData, + role: executionRole, + keyName: "S3BenchmarkKeyPair", + securityGroup, + vpcSubnets: { subnets: vpc.publicSubnets }, + requireImdsv2: true, + }); + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/package-lock.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/package-lock.json new file mode 100644 index 0000000000..4e749c56b5 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/package-lock.json @@ -0,0 +1,9757 @@ +{ + "name": "infrastructure", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "infrastructure", + "version": "1.0.0", + "license": "Apache-2.0", + "devDependencies": { + "@types/node": "20.1.7", + "@typescript-eslint/eslint-plugin": "^5.59.9", + "@typescript-eslint/parser": "^5.59.9", + "aws-cdk": "^2.82.0", + "aws-cdk-lib": "^2.82.0", + "constructs": "^10.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^8.8.0", + "prettier": "^2.8.8", + "source-map-support": "^0.5.21", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.1.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.186", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.186.tgz", + "integrity": "sha512-2wSuOWQlrWc0AFuPCzXYn2Y8oK2vTfpNrVa8dxBxfswbwUrXMAirhpsP1f1J/4KEhA/4Hs4l27dKiC/IcDrvIQ==", + "dev": true + }, + "node_modules/@aws-cdk/asset-kubectl-v20": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", + "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==", + "dev": true + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { + "version": "2.0.155", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.155.tgz", + "integrity": "sha512-Q+Ny25hUPINlBbS6lmbUr4m6Tr6ToEJBla7sXA3FO3JUD0Z69ddcgbhuEBF8Rh1a2xmPONm89eX77kwK2fb4vQ==", + "dev": true + }, + "node_modules/@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz", + "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz", + "integrity": "sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==", + "dev": true, + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.0", + "@babel/helper-compilation-targets": "^7.22.1", + "@babel/helper-module-transforms": "^7.22.1", + "@babel/helpers": "^7.22.0", + "@babel/parser": "^7.22.0", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + }, + "node_modules/@babel/generator": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz", + "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.22.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz", + "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.22.0", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz", + "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.21.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz", + "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-simple-access": "^7.21.5", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", + "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", + "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", + "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.3.tgz", + "integrity": "sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", + "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", + "dev": true, + "peer": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.21.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz", + "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/parser": "^7.21.9", + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz", + "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.3", + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.22.4", + "@babel/types": "^7.22.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", + "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.21.5", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "peer": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", + "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/reporters": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.5.0", + "jest-config": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-resolve-dependencies": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "jest-watcher": "^29.5.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "dev": true, + "peer": true, + "dependencies": { + "expect": "^29.5.0", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "peer": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", + "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/types": "^29.5.0", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", + "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "dev": true, + "peer": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", + "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", + "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", + "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true, + "peer": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "peer": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz", + "integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==", + "dev": true, + "peer": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.1.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.7.tgz", + "integrity": "sha512-WCuw/o4GSwDGMoonES8rcvwsig77dGCMbZDrZr2x4ZZiNW4P/gcoZXe/0twgtobcTkmg9TuKflxYL/DuwDyJzg==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "peer": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true, + "peer": true + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz", + "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/type-utils": "5.59.9", + "@typescript-eslint/utils": "5.59.9", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz", + "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz", + "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz", + "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/utils": "5.59.9", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz", + "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz", + "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz", + "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz", + "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.9", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "peer": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk": { + "version": "2.82.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.82.0.tgz", + "integrity": "sha512-4uAhKN8HMdxxM10Th8aMQJLSINO6evYV9UKTPL0hbVQ6dh6+i5LbSejcvDRw0HfBoP6qV1LNV8P8XGLYIC3tyQ==", + "dev": true, + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 14.15.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.82.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.82.0.tgz", + "integrity": "sha512-icLhHvoxxo5mu9z8oplSHF+A7scbRiXYoRp2hyFkYSCoY9H+eBeIVXKA2S5YPpJfJO4SeORbCQnsyXBbz31XXw==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml" + ], + "dev": true, + "dependencies": { + "@aws-cdk/asset-awscli-v1": "^2.2.177", + "@aws-cdk/asset-kubectl-v20": "^2.1.1", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", + "jsonschema": "^1.4.1", + "minimatch": "^3.1.2", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "dev": true, + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.2.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.5.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.8.1", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", + "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/transform": "^29.5.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.5.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz", + "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001489", + "electron-to-chromium": "^1.4.411", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "peer": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001495", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", + "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true, + "peer": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "peer": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true, + "peer": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/constructs": { + "version": "10.2.44", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.44.tgz", + "integrity": "sha512-7v4KnradGM1JeOuuhey4m9t6pLkwc0XOlgO1TrjB8HLRbfxDHeJ10Tt6IfxihgkrDjYN1SOMmpeVFzLLNjGRQw==", + "dev": true, + "engines": { + "node": ">= 16.14.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true, + "peer": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.422.tgz", + "integrity": "sha512-OQMid0IRbJv27BhlPiBK8CfGzjeq4ZCBSmpwNi1abyS8w17/BajOUu7hBI49ptDTBCz9NRFbORhWvt41dF7dwg==", + "dev": true, + "peer": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "peer": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "peer": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "peer": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "peer": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true, + "peer": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "peer": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "peer": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "peer": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "peer": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "peer": true + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "peer": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "peer": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "peer": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", + "import-local": "^3.0.2", + "jest-cli": "^29.5.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "peer": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", + "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.5.0", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.5.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", + "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "dev": true, + "peer": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", + "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.5.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", + "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", + "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", + "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "dev": true, + "peer": true, + "dependencies": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", + "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", + "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", + "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "dev": true, + "peer": true, + "dependencies": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", + "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/environment": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-leak-detector": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-resolve": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-util": "^29.5.0", + "jest-watcher": "^29.5.0", + "jest-worker": "^29.5.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", + "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/globals": "^29.5.0", + "@jest/source-map": "^29.4.3", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", + "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.5.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "peer": true + }, + "node_modules/jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", + "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", + "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.5.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.5.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "peer": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "peer": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "peer": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "peer": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "peer": true + }, + "node_modules/node-releases": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", + "dev": true, + "peer": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "peer": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "peer": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "peer": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "peer": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "peer": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true, + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "peer": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "peer": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true, + "peer": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "peer": true, + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "peer": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "peer": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "peer": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "peer": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "peer": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "peer": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "peer": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "peer": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "peer": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@aws-cdk/asset-awscli-v1": { + "version": "2.2.186", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.186.tgz", + "integrity": "sha512-2wSuOWQlrWc0AFuPCzXYn2Y8oK2vTfpNrVa8dxBxfswbwUrXMAirhpsP1f1J/4KEhA/4Hs4l27dKiC/IcDrvIQ==", + "dev": true + }, + "@aws-cdk/asset-kubectl-v20": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", + "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==", + "dev": true + }, + "@aws-cdk/asset-node-proxy-agent-v5": { + "version": "2.0.155", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.155.tgz", + "integrity": "sha512-Q+Ny25hUPINlBbS6lmbUr4m6Tr6ToEJBla7sXA3FO3JUD0Z69ddcgbhuEBF8Rh1a2xmPONm89eX77kwK2fb4vQ==", + "dev": true + }, + "@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, + "peer": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz", + "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==", + "dev": true, + "peer": true + }, + "@babel/core": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz", + "integrity": "sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==", + "dev": true, + "peer": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.0", + "@babel/helper-compilation-targets": "^7.22.1", + "@babel/helper-module-transforms": "^7.22.1", + "@babel/helpers": "^7.22.0", + "@babel/parser": "^7.22.0", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + } + } + }, + "@babel/generator": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz", + "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.22.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz", + "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/compat-data": "^7.22.0", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz", + "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==", + "dev": true, + "peer": true + }, + "@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.21.4" + } + }, + "@babel/helper-module-transforms": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz", + "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-simple-access": "^7.21.5", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", + "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "dev": true, + "peer": true + }, + "@babel/helper-simple-access": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", + "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.21.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", + "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "dev": true, + "peer": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "peer": true + }, + "@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "peer": true + }, + "@babel/helpers": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.3.tgz", + "integrity": "sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.3" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", + "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", + "dev": true, + "peer": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/template": { + "version": "7.21.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz", + "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.21.4", + "@babel/parser": "^7.21.9", + "@babel/types": "^7.21.5" + } + }, + "@babel/traverse": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz", + "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.3", + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.22.4", + "@babel/types": "^7.22.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", + "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-string-parser": "^7.21.5", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "peer": true + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "peer": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "peer": true + }, + "@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", + "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.5.0", + "@jest/reporters": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.5.0", + "jest-config": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-resolve-dependencies": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "jest-watcher": "^29.5.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + } + }, + "@jest/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "dev": true, + "peer": true, + "requires": { + "expect": "^29.5.0", + "jest-snapshot": "^29.5.0" + } + }, + "@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "peer": true, + "requires": { + "jest-get-type": "^29.4.3" + } + }, + "@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + } + }, + "@jest/globals": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", + "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/types": "^29.5.0", + "jest-mock": "^29.5.0" + } + }, + "@jest/reporters": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", + "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "dev": true, + "peer": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + } + }, + "@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.25.16" + } + }, + "@jest/source-map": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", + "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", + "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/test-result": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", + "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + } + }, + "@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "requires": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "peer": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true, + "peer": true + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "peer": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz", + "integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==", + "dev": true, + "peer": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "peer": true, + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "peer": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "peer": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "@types/node": { + "version": "20.1.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.7.tgz", + "integrity": "sha512-WCuw/o4GSwDGMoonES8rcvwsig77dGCMbZDrZr2x4ZZiNW4P/gcoZXe/0twgtobcTkmg9TuKflxYL/DuwDyJzg==", + "dev": true + }, + "@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "peer": true + }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true, + "peer": true + }, + "@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz", + "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/type-utils": "5.59.9", + "@typescript-eslint/utils": "5.59.9", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/parser": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz", + "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz", + "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz", + "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/utils": "5.59.9", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz", + "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz", + "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/utils": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz", + "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz", + "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.9", + "eslint-visitor-keys": "^3.3.0" + } + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "peer": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "peer": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "aws-cdk": { + "version": "2.82.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.82.0.tgz", + "integrity": "sha512-4uAhKN8HMdxxM10Th8aMQJLSINO6evYV9UKTPL0hbVQ6dh6+i5LbSejcvDRw0HfBoP6qV1LNV8P8XGLYIC3tyQ==", + "dev": true, + "requires": { + "fsevents": "2.3.2" + } + }, + "aws-cdk-lib": { + "version": "2.82.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.82.0.tgz", + "integrity": "sha512-icLhHvoxxo5mu9z8oplSHF+A7scbRiXYoRp2hyFkYSCoY9H+eBeIVXKA2S5YPpJfJO4SeORbCQnsyXBbz31XXw==", + "dev": true, + "requires": { + "@aws-cdk/asset-awscli-v1": "^2.2.177", + "@aws-cdk/asset-kubectl-v20": "^2.1.1", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", + "jsonschema": "^1.4.1", + "minimatch": "^3.1.2", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", + "yaml": "1.10.2" + }, + "dependencies": { + "@balena/dockerignore": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "ajv": { + "version": "8.12.0", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "case": { + "version": "1.6.3", + "bundled": true, + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, + "fs-extra": { + "version": "11.1.1", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "bundled": true, + "dev": true + }, + "ignore": { + "version": "5.2.4", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonschema": { + "version": "1.4.1", + "bundled": true, + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "bundled": true, + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "punycode": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "bundled": true, + "dev": true + }, + "semver": { + "version": "7.5.1", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "slice-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "table": { + "version": "6.8.1", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + } + }, + "universalify": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "yaml": { + "version": "1.10.2", + "bundled": true, + "dev": true + } + } + }, + "babel-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", + "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "dev": true, + "peer": true, + "requires": { + "@jest/transform": "^29.5.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "peer": true, + "requires": { + "babel-plugin-jest-hoist": "^29.5.0", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz", + "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==", + "dev": true, + "peer": true, + "requires": { + "caniuse-lite": "^1.0.30001489", + "electron-to-chromium": "^1.4.411", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "peer": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "peer": true + }, + "caniuse-lite": { + "version": "1.0.30001495", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", + "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==", + "dev": true, + "peer": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "peer": true + }, + "ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true, + "peer": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "peer": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "peer": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true, + "peer": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "constructs": { + "version": "10.2.44", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.44.tgz", + "integrity": "sha512-7v4KnradGM1JeOuuhey4m9t6pLkwc0XOlgO1TrjB8HLRbfxDHeJ10Tt6IfxihgkrDjYN1SOMmpeVFzLLNjGRQw==", + "dev": true + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true, + "peer": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "peer": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "peer": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "peer": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "electron-to-chromium": { + "version": "1.4.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.422.tgz", + "integrity": "sha512-OQMid0IRbJv27BhlPiBK8CfGzjeq4ZCBSmpwNi1abyS8w17/BajOUu7hBI49ptDTBCz9NRFbORhWvt41dF7dwg==", + "dev": true, + "peer": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "peer": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "peer": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "peer": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "peer": true + }, + "eslint": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + }, + "espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "peer": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "peer": true + }, + "expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "peer": true, + "requires": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "peer": true, + "requires": { + "bser": "2.1.1" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "peer": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true, + "peer": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "peer": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "peer": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "peer": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "peer": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "peer": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "peer": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "peer": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "peer": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "peer": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "peer": true + }, + "is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "peer": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "peer": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "peer": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "peer": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "peer": true + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "peer": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "peer": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "peer": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", + "import-local": "^3.0.2", + "jest-cli": "^29.5.0" + } + }, + "jest-changed-files": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "peer": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", + "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.5.0", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.5.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-cli": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", + "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "dev": true, + "peer": true, + "requires": { + "@jest/core": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + } + }, + "jest-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + } + }, + "jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "dev": true, + "peer": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", + "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.5.0", + "pretty-format": "^29.5.0" + } + }, + "jest-environment-node": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", + "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + } + }, + "jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "peer": true + }, + "jest-haste-map": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", + "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", + "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "dev": true, + "peer": true, + "requires": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + } + }, + "jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + } + }, + "jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "peer": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", + "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "dev": true, + "peer": true + }, + "jest-resolve": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", + "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", + "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "dev": true, + "peer": true, + "requires": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.5.0" + } + }, + "jest-runner": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", + "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.5.0", + "@jest/environment": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-leak-detector": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-resolve": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-util": "^29.5.0", + "jest-watcher": "^29.5.0", + "jest-worker": "^29.5.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "peer": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "jest-runtime": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", + "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/globals": "^29.5.0", + "@jest/source-map": "^29.4.3", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", + "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.5.0", + "semver": "^7.3.5" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "peer": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "peer": true + } + } + }, + "jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", + "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.5.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true + } + } + }, + "jest-watcher": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", + "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "dev": true, + "peer": true, + "requires": { + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.5.0", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "peer": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.5.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "peer": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "peer": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "peer": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "peer": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "peer": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "peer": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "peer": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "peer": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "peer": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "peer": true + }, + "node-releases": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", + "dev": true, + "peer": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "peer": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "peer": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "peer": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "peer": true, + "requires": { + "p-limit": "^2.2.0" + }, + "dependencies": { + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "peer": true, + "requires": { + "p-try": "^2.0.0" + } + } + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "peer": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "peer": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true, + "peer": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "peer": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "peer": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "peer": true, + "requires": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "peer": true + } + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + }, + "pure-rand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "dev": true, + "peer": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true, + "peer": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "peer": true + }, + "resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "peer": true, + "requires": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "peer": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "peer": true + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "peer": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "peer": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "peer": true + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "peer": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "peer": true + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "peer": true, + "requires": { + "escape-string-regexp": "^2.0.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "peer": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "peer": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "peer": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "peer": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "peer": true + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "peer": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "peer": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-jest": { + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "peer": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "peer": true + }, + "typescript": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "peer": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + } + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "peer": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "peer": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "peer": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "peer": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/package.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/package.json new file mode 100644 index 0000000000..46e0b34ee2 --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/package.json @@ -0,0 +1,39 @@ +{ + "name": "infrastructure", + "version": "1.0.0", + "description": "CDK infrastructure for the S3 benchmark", + "main": "index.js", + "scripts": { + "format": "prettier --write '**/*.ts'", + "lint": "eslint --ext .ts lib", + "build": "tsc", + "watch": "tsc -w", + "cdk": "cdk" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/awslabs/smithy-rs.git" + }, + "author": "AWS Rust SDK Team", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/awslabs/smithy-rs/issues" + }, + "homepage": "https://github.com/awslabs/smithy-rs#readme", + "publish": false, + "devDependencies": { + "@types/node": "20.1.7", + "@typescript-eslint/eslint-plugin": "^5.59.9", + "@typescript-eslint/parser": "^5.59.9", + "aws-cdk": "^2.82.0", + "aws-cdk-lib": "^2.82.0", + "constructs": "^10.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^8.8.0", + "prettier": "^2.8.8", + "source-map-support": "^0.5.21", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.1.3" + } +} diff --git a/aws/sdk/benchmarks/s3-throughput/infrastructure/tsconfig.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/tsconfig.json new file mode 100644 index 0000000000..7ec537b68e --- /dev/null +++ b/aws/sdk/benchmarks/s3-throughput/infrastructure/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ], + "outDir": "./build" + }, + "exclude": [ + "node_modules", + "cdk.out", + "build" + ] +} diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 8db1cdc0ee..ebf1b3a705 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +import aws.sdk.AwsExamplesLayout import aws.sdk.AwsServices import aws.sdk.Membership -import aws.sdk.RootTest import aws.sdk.discoverServices import aws.sdk.docsLandingPage import aws.sdk.parseMembership @@ -21,6 +21,7 @@ plugins { configure { smithyBuildConfigs = files(buildDir.resolve("smithy-build.json")) + allowUnknownTraits = true } val smithyVersion: String by project @@ -60,6 +61,7 @@ val crateVersioner by lazy { aws.sdk.CrateVersioner.defaultFor(rootProject, prop fun getRustMSRV(): String = properties.get("rust.msrv") ?: throw Exception("Rust MSRV missing") fun getPreviousReleaseVersionManifestPath(): String? = properties.get("aws.sdk.previous.release.versions.manifest") +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "orchestrator" fun loadServiceMembership(): Membership { val membershipOverride = properties.get("aws.services")?.let { parseMembership(it) } @@ -99,11 +101,12 @@ fun generateSmithyBuild(services: AwsServices): String { }, "codegen": { "includeFluentClient": false, + "includeEndpointUrlConfig": false, "renameErrors": false, "debugMode": $debugMode, "eventStreamAllowList": [$eventStreamAllowListMembers], - "enableNewCrateOrganizationScheme": true, - "enableNewSmithyRuntime": false + "enableNewSmithyRuntime": "${getSmithyRuntimeMode()}", + "enableUserConfigurableRuntimePlugins": false }, "service": "${service.service}", "module": "$moduleName", @@ -151,6 +154,8 @@ tasks.register("generateSmithyBuild") { } tasks.register("generateIndexMd") { + dependsOn("smithyBuildJar") + inputs.property("servicelist", awsServices.services.toString()) val indexMd = outputDir.resolve("index.md") outputs.file(indexMd) @@ -161,6 +166,8 @@ tasks.register("generateIndexMd") { tasks.register("relocateServices") { description = "relocate AWS services to their final destination" + dependsOn("smithyBuildJar") + doLast { awsServices.services.forEach { logger.info("Relocating ${it.module}...") @@ -186,6 +193,8 @@ tasks.register("relocateServices") { tasks.register("relocateExamples") { description = "relocate the examples folder & rewrite path dependencies" + dependsOn("smithyBuildJar") + doLast { if (awsServices.examples.isNotEmpty()) { copy { @@ -195,6 +204,7 @@ tasks.register("relocateExamples") { } into(outputDir) exclude("**/target") + exclude("**/rust-toolchain.toml") filter { line -> line.replace("build/aws-sdk/sdk/", "sdk/") } } } @@ -207,6 +217,8 @@ tasks.register("relocateExamples") { tasks.register("relocateTests") { description = "relocate the root integration tests and rewrite path dependencies" + dependsOn("smithyBuildJar") + doLast { if (awsServices.rootTests.isNotEmpty()) { copy { @@ -230,16 +242,26 @@ tasks.register("relocateTests") { tasks.register("fixExampleManifests") { description = "Adds dependency path and corrects version number of examples after relocation" enabled = awsServices.examples.isNotEmpty() + dependsOn("relocateExamples") toolPath = sdkVersionerToolPath binaryName = "sdk-versioner" - arguments = listOf( - "use-path-and-version-dependencies", - "--isolate-crates", - "--sdk-path", "../../sdk", - "--versions-toml", outputDir.resolve("versions.toml").absolutePath, - outputDir.resolve("examples").absolutePath, - ) + arguments = when (AwsExamplesLayout.detect(project)) { + AwsExamplesLayout.Flat -> listOf( + "use-path-and-version-dependencies", + "--isolate-crates", + "--sdk-path", "../../sdk", + "--versions-toml", outputDir.resolve("versions.toml").absolutePath, + outputDir.resolve("examples").absolutePath, + ) + AwsExamplesLayout.Workspaces -> listOf( + "use-path-and-version-dependencies", + "--isolate-crates", + "--sdk-path", sdkOutputDir.absolutePath, + "--versions-toml", outputDir.resolve("versions.toml").absolutePath, + outputDir.resolve("examples").absolutePath, + ) + } outputs.dir(outputDir) dependsOn("relocateExamples", "generateVersionManifest") @@ -257,6 +279,7 @@ fun rewritePathDependency(line: String): String { } tasks.register("copyAllRuntimes") { + dependsOn("smithyBuildJar") from("$rootDir/aws/rust-runtime") { CrateSet.AWS_SDK_RUNTIME.forEach { include("$it/**") } } @@ -293,6 +316,7 @@ tasks.register("relocateRuntime") { } tasks.register("relocateChangelog") { + dependsOn("smithyBuildJar") from("$rootDir/aws") include("SDK_CHANGELOG.md") into(outputDir) @@ -302,9 +326,9 @@ tasks.register("relocateChangelog") { fun generateCargoWorkspace(services: AwsServices): String { return """ |[workspace] - |exclude = [${"\n"}${services.rootTests.map(RootTest::manifestName).joinToString(",\n") { "| \"$it\"" }} + |exclude = [${"\n"}${services.excludedFromWorkspace().joinToString(",\n") { "| \"$it\"" }} |] - |members = [${"\n"}${services.allModules.joinToString(",\n") { "| \"$it\"" }} + |members = [${"\n"}${services.includedInWorkspace().joinToString(",\n") { "| \"$it\"" }} |] """.trimMargin() } @@ -314,6 +338,7 @@ tasks.register("generateCargoWorkspace") { doFirst { outputDir.mkdirs() outputDir.resolve("Cargo.toml").writeText(generateCargoWorkspace(awsServices)) + rootProject.rootDir.resolve("clippy-root.toml").copyTo(outputDir.resolve("clippy.toml")) } inputs.property("servicelist", awsServices.moduleNames.toString()) if (awsServices.examples.isNotEmpty()) { @@ -323,11 +348,17 @@ tasks.register("generateCargoWorkspace") { inputs.dir(test.path) } outputs.file(outputDir.resolve("Cargo.toml")) + outputs.file(outputDir.resolve("clippy.toml")) outputs.upToDateWhen { false } } tasks.register("fixManifests") { description = "Run the publisher tool's `fix-manifests` sub-command on the generated services" + dependsOn("relocateServices") + dependsOn("relocateRuntime") + dependsOn("relocateAwsRuntime") + dependsOn("relocateExamples") + dependsOn("relocateTests") inputs.dir(publisherToolPath) outputs.dir(outputDir) @@ -339,18 +370,10 @@ tasks.register("fixManifests") { add("--disable-version-number-validation") } } - - dependsOn("assemble") - dependsOn("relocateServices") - dependsOn("relocateRuntime") - dependsOn("relocateAwsRuntime") - dependsOn("relocateExamples") - dependsOn("relocateTests") } tasks.register("hydrateReadme") { description = "Run the publisher tool's `hydrate-readme` sub-command to create the final AWS Rust SDK README file" - dependsOn("generateVersionManifest") inputs.dir(publisherToolPath) @@ -385,7 +408,9 @@ tasks.register("generateVersionManifest") { binaryName = "publisher" arguments = mutableListOf( "generate-version-manifest", - "--location", + "--input-location", + sdkOutputDir.absolutePath, + "--output-location", outputDir.absolutePath, "--smithy-build", buildDir.resolve("smithy-build.json").normalize().absolutePath, @@ -399,10 +424,17 @@ tasks.register("generateVersionManifest") { } } -tasks.register("finalizeSdk") { - dependsOn("assemble") +tasks["smithyBuildJar"].apply { + inputs.file(buildDir.resolve("smithy-build.json")) + inputs.dir(projectDir.resolve("aws-models")) + dependsOn("generateSmithyBuild") + dependsOn("generateCargoWorkspace") outputs.upToDateWhen { false } - finalizedBy( +} +tasks["assemble"].apply { + dependsOn( + "deleteSdk", + "smithyBuildJar", "relocateServices", "relocateRuntime", "relocateAwsRuntime", @@ -415,28 +447,22 @@ tasks.register("finalizeSdk") { "hydrateReadme", "relocateChangelog", ) -} - -tasks["smithyBuildJar"].apply { - inputs.file(buildDir.resolve("smithy-build.json")) - inputs.dir(projectDir.resolve("aws-models")) - dependsOn("generateSmithyBuild") - dependsOn("generateCargoWorkspace") outputs.upToDateWhen { false } } -tasks["assemble"].apply { - dependsOn("deleteSdk") - dependsOn("smithyBuildJar") - finalizedBy("finalizeSdk") -} project.registerCargoCommandsTasks(outputDir, defaultRustDocFlags) project.registerGenerateCargoConfigTomlTask(outputDir) +tasks["test"].dependsOn("assemble") tasks["test"].finalizedBy(Cargo.CLIPPY.toString, Cargo.TEST.toString, Cargo.DOCS.toString) tasks.register("deleteSdk") { - delete = setOf(outputDir) + delete( + fileTree(outputDir) { + // Delete files but keep directories so that terminals don't get messed up in local development + include("**/*.*") + }, + ) } tasks["clean"].dependsOn("deleteSdk") tasks["clean"].doFirst { diff --git a/aws/sdk/gradle.properties b/aws/sdk/gradle.properties index 1cd163ac60..3bd3028606 100644 --- a/aws/sdk/gradle.properties +++ b/aws/sdk/gradle.properties @@ -3,8 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 # -# timestream requires endpoint discovery: https://github.com/awslabs/aws-sdk-rust/issues/114 -aws.services=-timestreamwrite,-timestreamquery +aws.services= # List of services to generate Event Stream operations for: aws.services.eventstream.allowlist=\ diff --git a/aws/sdk/integration-tests/Cargo.toml b/aws/sdk/integration-tests/Cargo.toml index 7410b5f824..284bc1bcb1 100644 --- a/aws/sdk/integration-tests/Cargo.toml +++ b/aws/sdk/integration-tests/Cargo.toml @@ -15,6 +15,6 @@ members = [ "s3control", "sts", "transcribestreaming", - "using-native-tls-instead-of-rustls", + "timestreamquery", "webassembly", ] diff --git a/aws/sdk/integration-tests/dynamodb/Cargo.toml b/aws/sdk/integration-tests/dynamodb/Cargo.toml index 7801137286..61663dbbed 100644 --- a/aws/sdk/integration-tests/dynamodb/Cargo.toml +++ b/aws/sdk/integration-tests/dynamodb/Cargo.toml @@ -11,14 +11,18 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +approx = "0.5.1" +aws-config = { path = "../../build/aws-sdk/sdk/aws-config" } aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } aws-sdk-dynamodb = { path = "../../build/aws-sdk/sdk/dynamodb" } +aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } -aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test" } -aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" } +aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["test-util"]} +aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["test-util"]} +aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types", features = ["test-util"]} aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1.0.0" criterion = { version = "0.4.0" } @@ -26,8 +30,8 @@ futures-util = { version = "0.3.16", default-features = false } http = "0.2.0" serde_json = "1.0.0" tokio = { version = "1.23.1", features = ["full", "test-util"] } -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } tokio-stream = "0.1.5" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } [[bench]] name = "deserialization_bench" diff --git a/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs b/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs index faf7eba2ec..6b83b189d5 100644 --- a/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs +++ b/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs @@ -3,13 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_dynamodb::operation::query::Query; -use aws_smithy_http::response::ParseHttpResponse; -use bytes::Bytes; use criterion::{criterion_group, criterion_main, Criterion}; fn do_bench() { - let response = http::Response::builder() + #[cfg(aws_sdk_middleware_mode)] + { + use aws_sdk_dynamodb::operation::query::Query; + use aws_smithy_http::response::ParseHttpResponse; + use bytes::Bytes; + + let response = http::Response::builder() .header("server", "Server") .header("date", "Mon, 08 Mar 2021 15:51:23 GMT") .header("content-type", "application/x-amz-json-1.0") @@ -21,9 +24,10 @@ fn do_bench() { .body(Bytes::copy_from_slice(br#"{"Count":2,"Items":[{"year":{"N":"2013"},"info":{"M":{"actors":{"L":[{"S":"Daniel Bruhl"},{"S":"Chris Hemsworth"},{"S":"Olivia Wilde"}]},"plot":{"S":"A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda."},"release_date":{"S":"2013-09-02T00:00:00Z"},"image_url":{"S":"http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg"},"genres":{"L":[{"S":"Action"},{"S":"Biography"},{"S":"Drama"},{"S":"Sport"}]},"directors":{"L":[{"S":"Ron Howard"}]},"rating":{"N":"8.3"},"rank":{"N":"2"},"running_time_secs":{"N":"7380"}}},"title":{"S":"Rush"}},{"year":{"N":"2013"},"info":{"M":{"actors":{"L":[{"S":"David Matthewman"},{"S":"Ann Thomas"},{"S":"Jonathan G. Neff"}]},"release_date":{"S":"2013-01-18T00:00:00Z"},"plot":{"S":"A rock band plays their music at high volumes, annoying the neighbors."},"genres":{"L":[{"S":"Comedy"},{"S":"Drama"}]},"image_url":{"S":"http://ia.media-imdb.com/images/N/O9ERWAU7FS797AJ7LU8HN09AMUP908RLlo5JF90EWR7LJKQ7@@._V1_SX400_.jpg"},"directors":{"L":[{"S":"Alice Smith"},{"S":"Bob Jones"}]},"rating":{"N":"6.2"},"rank":{"N":"11"},"running_time_secs":{"N":"5215"}}},"title":{"S":"Turn It Down, Or Else!"}}],"ScannedCount":2}"#)) .unwrap(); - let parser = Query::new(); - let output = ::parse_loaded(&parser, &response).unwrap(); - assert_eq!(2, output.count); + let parser = Query::new(); + let output = ::parse_loaded(&parser, &response).unwrap(); + assert_eq!(2, output.count); + } } fn bench_group(c: &mut Criterion) { diff --git a/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs b/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs index 82e37cfe8b..f103c07c00 100644 --- a/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs +++ b/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs @@ -7,7 +7,6 @@ use aws_sdk_dynamodb::operation::put_item::PutItemInput; use aws_sdk_dynamodb::types::AttributeValue; use aws_sdk_dynamodb::Config; use criterion::{criterion_group, criterion_main, Criterion}; -use futures_util::FutureExt; macro_rules! attr_s { ($str_val:expr) => { @@ -34,15 +33,20 @@ macro_rules! attr_obj { }; } -fn do_bench(config: &Config, input: &PutItemInput) { - let operation = input - .make_operation(&config) - .now_or_never() - .unwrap() - .expect("operation failed to build"); - let (http_request, _parts) = operation.into_request_response().0.into_parts(); - let body = http_request.body().bytes().unwrap(); - assert_eq!(body[0], b'{'); +fn do_bench(_config: &Config, _input: &PutItemInput) { + #[cfg(aws_sdk_middleware_mode)] + { + use futures_util::FutureExt; + + let operation = _input + .make_operation(&_config) + .now_or_never() + .unwrap() + .expect("operation failed to build"); + let (http_request, _parts) = operation.into_request_response().0.into_parts(); + let body = http_request.body().bytes().unwrap(); + assert_eq!(body[0], b'{'); + } } fn bench_group(c: &mut Criterion) { diff --git a/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs b/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs index 6a01fedefd..25d65bb94c 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs @@ -7,7 +7,6 @@ use aws_sdk_dynamodb::config::{self, Credentials, Region}; use aws_types::SdkConfig; use http::Uri; -#[track_caller] async fn expect_uri( conf: SdkConfig, uri: &'static str, diff --git a/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs b/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs new file mode 100644 index 0000000000..162c1fc50b --- /dev/null +++ b/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs @@ -0,0 +1,158 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(not(aws_sdk_middleware_mode))] +mod test { + use aws_sdk_dynamodb::config::{Credentials, Region, SharedAsyncSleep}; + use aws_sdk_dynamodb::{config::retry::RetryConfig, error::ProvideErrorMetadata}; + use aws_smithy_async::test_util::instant_time_and_sleep; + use aws_smithy_async::time::SharedTimeSource; + use aws_smithy_client::test_connection::TestConnection; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime::client::retries::RetryPartition; + use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; + use std::time::{Duration, SystemTime}; + + fn req() -> HttpRequest { + http::Request::builder() + .body(SdkBody::from("request body")) + .unwrap() + } + + fn ok() -> HttpResponse { + http::Response::builder() + .status(200) + .header("server", "Server") + .header("content-type", "application/x-amz-json-1.0") + .header("content-length", "23") + .header("connection", "keep-alive") + .header("x-amz-crc32", "2335643545") + .body(SdkBody::from("{ \"TableNames\": [ \"Test\" ] }")) + .unwrap() + } + + fn err() -> HttpResponse { + http::Response::builder() + .status(500) + .body(SdkBody::from("{ \"message\": \"The request has failed because of an unknown error, exception or failure.\", \"code\": \"InternalServerError\" }")) + .unwrap() + } + + fn throttling_err() -> HttpResponse { + http::Response::builder() + .status(400) + .body(SdkBody::from("{ \"message\": \"The request was denied due to request throttling.\", \"code\": \"ThrottlingException\" }")) + .unwrap() + } + + #[tokio::test] + async fn test_adaptive_retries_with_no_throttling_errors() { + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + + let events = vec![ + // First operation + (req(), err()), + (req(), err()), + (req(), ok()), + // Second operation + (req(), err()), + (req(), ok()), + // Third operation will fail, only errors + (req(), err()), + (req(), err()), + (req(), err()), + (req(), err()), + ]; + + let conn = TestConnection::new(events); + let config = aws_sdk_dynamodb::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .retry_config( + RetryConfig::adaptive() + .with_max_attempts(4) + .with_use_static_exponential_base(true), + ) + .time_source(SharedTimeSource::new(time_source)) + .sleep_impl(SharedAsyncSleep::new(sleep_impl.clone())) + .retry_partition(RetryPartition::new( + "test_adaptive_retries_with_no_throttling_errors", + )) + .http_connector(conn.clone()) + .build(); + let expected_table_names = vec!["Test".to_owned()]; + + // We create a new client each time to ensure that the cross-client retry state is working. + let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); + let res = client.list_tables().send().await.unwrap(); + assert_eq!(sleep_impl.total_duration(), Duration::from_secs(3)); + assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); + // Three requests should have been made, two failing & one success + assert_eq!(conn.requests().len(), 3); + + let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); + let res = client.list_tables().send().await.unwrap(); + assert_eq!(sleep_impl.total_duration(), Duration::from_secs(3 + 1)); + assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); + // Two requests should have been made, one failing & one success (plus previous requests) + assert_eq!(conn.requests().len(), 5); + + let client = aws_sdk_dynamodb::Client::from_conf(config); + let err = client.list_tables().send().await.unwrap_err(); + assert_eq!(sleep_impl.total_duration(), Duration::from_secs(3 + 1 + 7),); + assert_eq!(err.code(), Some("InternalServerError")); + // four requests should have been made, all failing (plus previous requests) + assert_eq!(conn.requests().len(), 9); + } + + #[tokio::test] + async fn test_adaptive_retries_with_throttling_errors() { + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + + let events = vec![ + // First operation + (req(), throttling_err()), + (req(), throttling_err()), + (req(), ok()), + // Second operation + (req(), err()), + (req(), ok()), + ]; + + let conn = TestConnection::new(events); + let config = aws_sdk_dynamodb::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .retry_config( + RetryConfig::adaptive() + .with_max_attempts(4) + .with_use_static_exponential_base(true), + ) + .time_source(SharedTimeSource::new(time_source)) + .sleep_impl(SharedAsyncSleep::new(sleep_impl.clone())) + .retry_partition(RetryPartition::new( + "test_adaptive_retries_with_throttling_errors", + )) + .http_connector(conn.clone()) + .build(); + let expected_table_names = vec!["Test".to_owned()]; + + // We create a new client each time to ensure that the cross-client retry state is working. + let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); + let res = client.list_tables().send().await.unwrap(); + assert_eq!(sleep_impl.total_duration(), Duration::from_secs(40)); + assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); + // Three requests should have been made, two failing & one success + assert_eq!(conn.requests().len(), 3); + + let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); + let res = client.list_tables().send().await.unwrap(); + assert!(Duration::from_secs(48) < sleep_impl.total_duration()); + assert!(Duration::from_secs(49) > sleep_impl.total_duration()); + assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); + // Two requests should have been made, one failing & one success (plus previous requests) + assert_eq!(conn.requests().len(), 5); + } +} diff --git a/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs b/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs index b566c598c7..d1a9b9369e 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::sync::Arc; use std::time::Duration; use aws_credential_types::provider::SharedCredentialsProvider; use aws_credential_types::Credentials; use aws_sdk_dynamodb::error::SdkError; -use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_client::never::NeverConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -37,7 +36,7 @@ async fn api_call_timeout_retries() { .build(), ) .retry_config(RetryConfig::standard()) - .sleep_impl(Arc::new(InstantSleep)) + .sleep_impl(SharedAsyncSleep::new(InstantSleep)) .build(); let client = aws_sdk_dynamodb::Client::from_conf(aws_sdk_dynamodb::Config::new(&conf)); let resp = client @@ -70,7 +69,7 @@ async fn no_retries_on_operation_timeout() { .build(), ) .retry_config(RetryConfig::standard()) - .sleep_impl(Arc::new(InstantSleep)) + .sleep_impl(SharedAsyncSleep::new(InstantSleep)) .build(); let client = aws_sdk_dynamodb::Client::from_conf(aws_sdk_dynamodb::Config::new(&conf)); let resp = client diff --git a/aws/sdk/integration-tests/glacier/tests/custom-headers.rs b/aws/sdk/integration-tests/glacier/tests/custom-headers.rs index 00163b0a81..941ed0a999 100644 --- a/aws/sdk/integration-tests/glacier/tests/custom-headers.rs +++ b/aws/sdk/integration-tests/glacier/tests/custom-headers.rs @@ -39,3 +39,49 @@ async fn set_correct_headers() { ], )); } + +#[tokio::test] +async fn autofill_account_id() { + let (conn, handler) = capture_request(None); + let conf = aws_sdk_glacier::Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .http_connector(conn) + .build(); + + let client = aws_sdk_glacier::Client::from_conf(conf); + let _resp = client + .abort_multipart_upload() + .vault_name("vault") + .upload_id("some/upload/id") + .send() + .await; + let req = handler.expect_request(); + assert_eq!( + "/-/vaults/vault/multipart-uploads/some%2Fupload%2Fid", + req.uri().path() + ); +} + +#[tokio::test] +async fn api_version_set() { + let (conn, handler) = capture_request(None); + let conf = aws_sdk_glacier::Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .http_connector(conn) + .build(); + + let client = aws_sdk_glacier::Client::from_conf(conf); + let _resp = client + .abort_multipart_upload() + .vault_name("vault") + .upload_id("some/upload/id") + .send() + .await; + let req = handler.expect_request(); + assert_ok(validate_headers( + req.headers(), + [("x-amz-glacier-version", "2012-06-01")], + )); +} diff --git a/aws/sdk/integration-tests/kms/Cargo.toml b/aws/sdk/integration-tests/kms/Cargo.toml index 8820956d27..2c76644e94 100644 --- a/aws/sdk/integration-tests/kms/Cargo.toml +++ b/aws/sdk/integration-tests/kms/Cargo.toml @@ -13,11 +13,14 @@ publish = false [dev-dependencies] aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } +aws-runtime = { path = "../../build/aws-sdk/sdk/aws-runtime" } aws-sdk-kms = { path = "../../build/aws-sdk/sdk/kms" } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } +aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } +aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["test-util"] } bytes = "1.0.0" http = "0.2.0" -tokio = { version = "1.23.1", features = ["full", "test-util"]} +tokio = { version = "1.23.1", features = ["full", "test-util"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/sdk/integration-tests/kms/tests/integration.rs b/aws/sdk/integration-tests/kms/tests/integration.rs index 8d17e13d48..ba2744f1f3 100644 --- a/aws/sdk/integration-tests/kms/tests/integration.rs +++ b/aws/sdk/integration-tests/kms/tests/integration.rs @@ -3,21 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_http::user_agent::AwsUserAgent; use aws_sdk_kms as kms; -use aws_sdk_kms::middleware::DefaultMiddleware; use aws_sdk_kms::operation::RequestId; use aws_smithy_client::test_connection::TestConnection; -use aws_smithy_client::{Client as CoreClient, SdkError}; +use aws_smithy_client::SdkError; use aws_smithy_http::body::SdkBody; use http::header::AUTHORIZATION; use http::Uri; use kms::config::{Config, Credentials, Region}; -use kms::operation::generate_random::GenerateRandomInput; use std::time::{Duration, UNIX_EPOCH}; -type Client = CoreClient; - // TODO(DVR): having the full HTTP requests right in the code is a bit gross, consider something // like https://github.com/davidbarsky/sigv4/blob/master/aws-sigv4/src/lib.rs#L283-L315 to store // the requests/responses externally @@ -34,9 +29,9 @@ async fn generate_random_cn() { .body(r#"{"Plaintext":"6CG0fbzzhg5G2VcFCPmJMJ8Njv3voYCgrGlp3+BZe7eDweCXgiyDH9BnkKvLmS7gQhnYDUlyES3fZVGwv5+CxA=="}"#).unwrap()) ]); let conf = Config::builder() + .http_connector(conn.clone()) .region(Region::new("cn-north-1")) .credentials_provider(Credentials::for_tests()) - .http_connector(conn.clone()) .build(); let client = kms::Client::from_conf(conf); let _ = client @@ -68,22 +63,27 @@ async fn generate_random() { .status(http::StatusCode::from_u16(200).unwrap()) .body(r#"{"Plaintext":"6CG0fbzzhg5G2VcFCPmJMJ8Njv3voYCgrGlp3+BZe7eDweCXgiyDH9BnkKvLmS7gQhnYDUlyES3fZVGwv5+CxA=="}"#).unwrap()) ]); - let client = Client::new(conn.clone()); let conf = Config::builder() + .http_connector(conn.clone()) .region(Region::new("us-east-1")) .credentials_provider(Credentials::for_tests()) .build(); - let mut op = GenerateRandomInput::builder() + let client = kms::Client::from_conf(conf); + let resp = client + .generate_random() .number_of_bytes(64) - .build() - .unwrap() - .make_operation(&conf) + .customize() .await - .expect("valid operation"); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1614952162)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - let resp = client.call(op).await.expect("request should succeed"); + .expect("customizable") + .mutate_request(|req| { + // Remove the invocation ID since the signed request above doesn't have it + req.headers_mut().remove("amz-sdk-invocation-id"); + }) + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1614952162)) + .user_agent_for_tests() + .send() + .await + .expect("request should succeed"); // primitive checksum assert_eq!( resp.plaintext @@ -106,27 +106,22 @@ async fn generate_random_malformed_response() { // last `}` replaced with a space, invalid JSON .body(r#"{"Plaintext":"6CG0fbzzhg5G2VcFCPmJMJ8Njv3voYCgrGlp3+BZe7eDweCXgiyDH9BnkKvLmS7gQhnYDUlyES3fZVGwv5+CxA==" "#).unwrap()) ]); - let client = Client::new(conn.clone()); let conf = Config::builder() + .http_connector(conn.clone()) .region(Region::new("us-east-1")) .credentials_provider(Credentials::for_tests()) .build(); - let op = GenerateRandomInput::builder() + let client = kms::Client::from_conf(conf); + client + .generate_random() .number_of_bytes(64) - .build() - .unwrap() - .make_operation(&conf) + .send() .await - .expect("valid operation"); - client.call(op).await.expect_err("response was malformed"); + .expect_err("response was malformed"); } #[tokio::test] async fn generate_random_keystore_not_found() { - let conf = Config::builder() - .region(Region::new("us-east-1")) - .credentials_provider(Credentials::for_tests()) - .build(); let conn = TestConnection::new(vec![( http::Request::builder() .header("content-type", "application/x-amz-json-1.1") @@ -150,21 +145,26 @@ async fn generate_random_keystore_not_found() { .header("content-length", "44") .body(r#"{"__type":"CustomKeyStoreNotFoundException"}"#).unwrap()) ]); + let conf = Config::builder() + .http_connector(conn.clone()) + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .build(); + let client = kms::Client::from_conf(conf); - let mut op = GenerateRandomInput::builder() + let err = client + .generate_random() .number_of_bytes(64) .custom_key_store_id("does not exist") - .build() - .unwrap() - .make_operation(&conf) + .customize() + .await + .expect("customizable") + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1614955644)) + .user_agent_for_tests() + .send() .await - .expect("valid operation"); + .expect_err("key store doesn't exist"); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1614955644)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - let client = Client::new(conn.clone()); - let err = client.call(op).await.expect_err("key store doesn't exist"); let inner = match err { SdkError::ServiceError(context) => context.into_err(), other => panic!("Incorrect error received: {:}", other), diff --git a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs new file mode 100644 index 0000000000..ba9090837c --- /dev/null +++ b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs @@ -0,0 +1,146 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(aws_sdk_middleware_mode)] +mod middleware_mode_tests { + use aws_http::retry::AwsResponseRetryClassifier; + use aws_sdk_kms as kms; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::operation::{self, Parts}; + use aws_smithy_http::response::ParseStrictResponse; + use aws_smithy_http::result::SdkError; + use aws_smithy_http::retry::ClassifyRetry; + use aws_smithy_types::retry::{ErrorKind, RetryKind}; + use bytes::Bytes; + use kms::operation::create_alias::{CreateAlias, CreateAliasInput}; + + async fn create_alias_op() -> Parts { + let conf = kms::Config::builder().build(); + let (_, parts) = CreateAliasInput::builder() + .build() + .unwrap() + .make_operation(&conf) + .await + .expect("valid request") + .into_request_response(); + parts + } + + /// Parse a semi-real response body and assert that the correct retry status is returned + #[tokio::test] + async fn errors_are_retryable() { + let op = create_alias_op().await; + let http_response = http::Response::builder() + .status(400) + .body(Bytes::from_static( + br#"{ "code": "LimitExceededException" }"#, + )) + .unwrap(); + let err = op.response_handler.parse(&http_response).map_err(|e| { + SdkError::service_error( + e, + operation::Response::new(http_response.map(SdkBody::from)), + ) + }); + let retry_kind = op.retry_classifier.classify_retry(err.as_ref()); + assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); + } + + #[tokio::test] + async fn unmodeled_errors_are_retryable() { + let op = create_alias_op().await; + let http_response = http::Response::builder() + .status(400) + .body(Bytes::from_static(br#"{ "code": "ThrottlingException" }"#)) + .unwrap(); + let err = op.response_handler.parse(&http_response).map_err(|e| { + SdkError::service_error( + e, + operation::Response::new(http_response.map(SdkBody::from)), + ) + }); + let retry_kind = op.retry_classifier.classify_retry(err.as_ref()); + assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); + } +} + +#[cfg(not(aws_sdk_middleware_mode))] +mod orchestrator_mode_tests { + use aws_credential_types::Credentials; + use aws_runtime::retries::classifier::AwsErrorCodeClassifier; + use aws_sdk_kms as kms; + use aws_smithy_client::test_connection::infallible_connection_fn; + use aws_smithy_http::result::SdkError; + use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, InterceptorContext}; + use aws_smithy_runtime_api::client::orchestrator::{HttpResponse, OrchestratorError}; + use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; + use aws_smithy_types::retry::ErrorKind; + use bytes::Bytes; + use kms::operation::create_alias::CreateAliasError; + + async fn make_err( + response: impl Fn() -> http::Response + Send + Sync + 'static, + ) -> SdkError { + let conn = infallible_connection_fn(move |_| response()); + let conf = kms::Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(kms::config::Region::from_static("us-east-1")) + .build(); + let client = kms::Client::from_conf(conf); + client + .create_alias() + .send() + .await + .expect_err("response was a failure") + } + + /// Parse a semi-real response body and assert that the correct retry status is returned + #[tokio::test] + async fn errors_are_retryable() { + let err = make_err(|| { + http::Response::builder() + .status(400) + .body(Bytes::from_static( + br#"{ "code": "LimitExceededException" }"#, + )) + .unwrap() + }) + .await; + + dbg!(&err); + let classifier = AwsErrorCodeClassifier::::new(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + let err = err.into_service_error(); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase(err)))); + let retry_kind = classifier.classify_retry(&ctx); + assert_eq!( + Some(RetryReason::Error(ErrorKind::ThrottlingError)), + retry_kind + ); + } + + #[tokio::test] + async fn unmodeled_errors_are_retryable() { + let err = make_err(|| { + http::Response::builder() + .status(400) + .body(Bytes::from_static(br#"{ "code": "ThrottlingException" }"#)) + .unwrap() + }) + .await; + + dbg!(&err); + let classifier = AwsErrorCodeClassifier::::new(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + let err = err.into_service_error(); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase(err)))); + let retry_kind = classifier.classify_retry(&ctx); + assert_eq!( + Some(RetryReason::Error(ErrorKind::ThrottlingError)), + retry_kind + ); + } +} diff --git a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs index 2de72f3279..6d60b0e33e 100644 --- a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs +++ b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs @@ -3,17 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_http::retry::AwsResponseRetryClassifier; use aws_sdk_kms as kms; -use aws_smithy_http::body::SdkBody; -use aws_smithy_http::operation::{self, Parts}; -use aws_smithy_http::response::ParseStrictResponse; -use aws_smithy_http::result::SdkError; -use aws_smithy_http::retry::ClassifyRetry; -use aws_smithy_types::retry::{ErrorKind, RetryKind}; -use bytes::Bytes; -use kms::operation::create_alias::{CreateAlias, CreateAliasError, CreateAliasInput}; -use kms::operation::generate_random::{GenerateRandom, GenerateRandomOutput}; +use kms::operation::generate_random::GenerateRandomOutput; use kms::primitives::Blob; #[test] @@ -21,104 +12,13 @@ fn validate_sensitive_trait() { let builder = GenerateRandomOutput::builder().plaintext(Blob::new("some output")); assert_eq!( format!("{:?}", builder), - "GenerateRandomOutputBuilder { plaintext: \"*** Sensitive Data Redacted ***\", _request_id: None }" + "GenerateRandomOutputBuilder { plaintext: \"*** Sensitive Data Redacted ***\", ciphertext_for_recipient: None, _request_id: None }" ); let output = GenerateRandomOutput::builder() .plaintext(Blob::new("some output")) .build(); assert_eq!( format!("{:?}", output), - "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\", _request_id: None }" + "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\", ciphertext_for_recipient: None, _request_id: None }" ); } - -fn assert_send_sync() {} -fn assert_send_fut(_: T) {} -fn assert_debug() {} - -#[tokio::test] -async fn types_are_send_sync() { - assert_send_sync::(); - assert_send_sync::>(); - assert_send_sync::(); - assert_send_sync::(); - assert_send_sync::(); - assert_send_sync::(); - let conf = kms::Config::builder().build(); - assert_send_fut(kms::Client::from_conf(conf).list_keys().send()); -} - -#[tokio::test] -async fn client_is_debug() { - let conf = kms::Config::builder().build(); - let client = kms::Client::from_conf(conf); - assert_ne!(format!("{:?}", client), ""); -} - -#[tokio::test] -async fn client_is_clone() { - let conf = kms::Config::builder().build(); - let client = kms::Client::from_conf(conf); - - fn is_clone(it: impl Clone) { - drop(it) - } - - is_clone(client); -} - -#[test] -fn types_are_debug() { - assert_debug::(); - assert_debug::(); - assert_debug::(); -} - -async fn create_alias_op() -> Parts { - let conf = kms::Config::builder().build(); - let (_, parts) = CreateAliasInput::builder() - .build() - .unwrap() - .make_operation(&conf) - .await - .expect("valid request") - .into_request_response(); - parts -} - -/// Parse a semi-real response body and assert that the correct retry status is returned -#[tokio::test] -async fn errors_are_retryable() { - let op = create_alias_op().await; - let http_response = http::Response::builder() - .status(400) - .body(Bytes::from_static( - br#"{ "code": "LimitExceededException" }"#, - )) - .unwrap(); - let err = op.response_handler.parse(&http_response).map_err(|e| { - SdkError::service_error( - e, - operation::Response::new(http_response.map(SdkBody::from)), - ) - }); - let retry_kind = op.retry_classifier.classify_retry(err.as_ref()); - assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); -} - -#[tokio::test] -async fn unmodeled_errors_are_retryable() { - let op = create_alias_op().await; - let http_response = http::Response::builder() - .status(400) - .body(Bytes::from_static(br#"{ "code": "ThrottlingException" }"#)) - .unwrap(); - let err = op.response_handler.parse(&http_response).map_err(|e| { - SdkError::service_error( - e, - operation::Response::new(http_response.map(SdkBody::from)), - ) - }); - let retry_kind = op.retry_classifier.classify_retry(err.as_ref()); - assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); -} diff --git a/aws/sdk/integration-tests/kms/tests/traits.rs b/aws/sdk/integration-tests/kms/tests/traits.rs new file mode 100644 index 0000000000..bfc1cf900b --- /dev/null +++ b/aws/sdk/integration-tests/kms/tests/traits.rs @@ -0,0 +1,50 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_sdk_kms as kms; +use kms::operation::create_alias::CreateAliasError; +use kms::operation::generate_random::GenerateRandom; + +fn assert_send_sync() {} +fn assert_send_fut(_: T) {} +fn assert_debug() {} + +#[tokio::test] +async fn types_are_send_sync() { + assert_send_sync::(); + assert_send_sync::>(); + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + let conf = kms::Config::builder().build(); + assert_send_fut(kms::Client::from_conf(conf).list_keys().send()); +} + +#[tokio::test] +async fn client_is_debug() { + let conf = kms::Config::builder().build(); + let client = kms::Client::from_conf(conf); + assert_ne!(format!("{:?}", client), ""); +} + +#[tokio::test] +async fn client_is_clone() { + let conf = kms::Config::builder().build(); + let client = kms::Client::from_conf(conf); + + fn is_clone(it: impl Clone) { + drop(it) + } + + is_clone(client); +} + +#[test] +fn types_are_debug() { + assert_debug::(); + assert_debug::(); + assert_debug::(); +} diff --git a/aws/sdk/integration-tests/lambda/Cargo.toml b/aws/sdk/integration-tests/lambda/Cargo.toml index 169327eb75..6e51d089f5 100644 --- a/aws/sdk/integration-tests/lambda/Cargo.toml +++ b/aws/sdk/integration-tests/lambda/Cargo.toml @@ -10,6 +10,7 @@ publish = false [dev-dependencies] async-stream = "0.3.0" aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } aws-sdk-lambda = { path = "../../build/aws-sdk/sdk/lambda" } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-eventstream = { path = "../../build/aws-sdk/sdk/aws-smithy-eventstream" } diff --git a/aws/sdk/integration-tests/lambda/tests/request_id.rs b/aws/sdk/integration-tests/lambda/tests/request_id.rs index 2bc465835e..0e071945aa 100644 --- a/aws/sdk/integration-tests/lambda/tests/request_id.rs +++ b/aws/sdk/integration-tests/lambda/tests/request_id.rs @@ -3,36 +3,62 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_lambda::operation::list_functions::{ListFunctions, ListFunctionsError}; +use aws_sdk_lambda::config::{Credentials, Region}; +use aws_sdk_lambda::operation::list_functions::ListFunctionsError; use aws_sdk_lambda::operation::RequestId; -use aws_smithy_http::response::ParseHttpResponse; -use bytes::Bytes; +use aws_sdk_lambda::{Client, Config}; +use aws_smithy_client::test_connection::infallible_connection_fn; -#[test] -fn get_request_id_from_unmodeled_error() { - let resp = http::Response::builder() - .header("x-amzn-RequestId", "correct-request-id") - .header("X-Amzn-Errortype", "ListFunctions") - .status(500) - .body("{}") - .unwrap(); - let err = ListFunctions::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect_err("status was 500, this is an error"); - assert!(matches!(err, ListFunctionsError::Unhandled(_))); - assert_eq!(Some("correct-request-id"), err.request_id()); - assert_eq!(Some("correct-request-id"), err.meta().request_id()); +async fn run_test( + response: impl Fn() -> http::Response<&'static str> + Send + Sync + 'static, + expect_error: bool, +) { + let conn = infallible_connection_fn(move |_| response()); + let conf = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::from_static("us-east-1")) + .build(); + let client = Client::from_conf(conf); + let resp = client.list_functions().send().await; + if expect_error { + let err = resp.err().expect("should be an error").into_service_error(); + assert!(matches!(err, ListFunctionsError::Unhandled(_))); + assert_eq!(Some("correct-request-id"), err.request_id()); + assert_eq!(Some("correct-request-id"), err.meta().request_id()); + } else { + let output = resp.expect("should be successful"); + assert_eq!(Some("correct-request-id"), output.request_id()); + } } -#[test] -fn get_request_id_from_successful_response() { - let resp = http::Response::builder() - .header("x-amzn-RequestId", "correct-request-id") - .status(200) - .body(r#"{"Functions":[],"NextMarker":null}"#) - .unwrap(); - let output = ListFunctions::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect("valid successful response"); - assert_eq!(Some("correct-request-id"), output.request_id()); +#[tokio::test] +async fn get_request_id_from_unmodeled_error() { + run_test( + || { + http::Response::builder() + .header("x-amzn-RequestId", "correct-request-id") + .header("X-Amzn-Errortype", "ListFunctions") + .status(500) + .body("{}") + .unwrap() + }, + true, + ) + .await; +} + +#[tokio::test] +async fn get_request_id_from_successful_response() { + run_test( + || { + http::Response::builder() + .header("x-amzn-RequestId", "correct-request-id") + .status(200) + .body(r#"{"Functions":[],"NextMarker":null}"#) + .unwrap() + }, + false, + ) + .await; } diff --git a/aws/sdk/integration-tests/no-default-features/Cargo.toml b/aws/sdk/integration-tests/no-default-features/Cargo.toml index 545837f66c..8408ac6694 100644 --- a/aws/sdk/integration-tests/no-default-features/Cargo.toml +++ b/aws/sdk/integration-tests/no-default-features/Cargo.toml @@ -17,6 +17,7 @@ publish = false aws-config = { path = "../../build/aws-sdk/sdk/aws-config", default-features = false } aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3", default-features = false } aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" } +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } futures = "0.3.25" tokio = { version = "1.23.1", features = ["full", "test-util"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs index 8f250f9322..26d84c96f6 100644 --- a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs +++ b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs @@ -3,31 +3,73 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_sdk_s3::config::{Config, Credentials, SharedAsyncSleep, Sleep}; +use aws_sdk_s3::error::DisplayErrorContext; +use aws_smithy_async::rt::sleep::AsyncSleep; +use std::time::Duration; + // This will fail due to lack of a connector when constructing the SDK Config +// If this test doesn't panic, you may have accidentally unified features, resulting in +// the connector being enabled transitively #[tokio::test] -#[should_panic( - expected = "No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this." -)] +#[should_panic(expected = "Enable the `rustls` crate feature or set a connector to fix this.")] async fn test_clients_from_sdk_config() { aws_config::load_from_env().await; } // This will fail due to lack of a connector when constructing the service client +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn test_clients_from_service_config() { + use aws_sdk_s3::config::Region; + + #[derive(Clone, Debug)] + struct StubSleep; + impl AsyncSleep for StubSleep { + fn sleep(&self, _duration: Duration) -> Sleep { + Sleep::new(Box::pin(async { /* no-op */ })) + } + } + + let config = Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .sleep_impl(SharedAsyncSleep::new(StubSleep)) + .build(); + // Creating the client shouldn't panic or error since presigning doesn't require a connector + let client = aws_sdk_s3::Client::from_conf(config); + + let err = client + .list_buckets() + .send() + .await + .expect_err("it should fail to send a request because there is no connector"); + let msg = format!("{}", DisplayErrorContext(err)); + assert!( + msg.contains("No HTTP connector was available to send this request. Enable the `rustls` crate feature or set a connector to fix this."), + "expected '{msg}' to contain 'No HTTP connector was available to send this request. Enable the `rustls` crate feature or set a connector to fix this.'" + ); +} + +// TODO(enableNewSmithyRuntimeMode): Remove this test (covered above for orchestrator) +// +// This will fail due to lack of a connector when constructing the service client +#[cfg(aws_sdk_middleware_mode)] #[tokio::test] #[should_panic( - expected = "No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this." + expected = "No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this." )] -async fn test_clients_from_service_config() { +async fn test_clients_from_service_config_middleware() { #[derive(Clone, Debug)] struct StubSleep; - impl aws_smithy_async::rt::sleep::AsyncSleep for StubSleep { - fn sleep(&self, _duration: std::time::Duration) -> aws_sdk_s3::config::Sleep { + impl AsyncSleep for StubSleep { + fn sleep(&self, _duration: Duration) -> Sleep { todo!() } } - let config = aws_sdk_s3::Config::builder() - .sleep_impl(std::sync::Arc::new(StubSleep {})) + let config = Config::builder() + .sleep_impl(SharedAsyncSleep::new(StubSleep {})) .build(); // This will panic due to the lack of an HTTP connector aws_sdk_s3::Client::from_conf(config); diff --git a/aws/sdk/integration-tests/polly/tests/presigning.rs b/aws/sdk/integration-tests/polly/tests/presigning.rs index bfb8729164..eaf525d7fb 100644 --- a/aws/sdk/integration-tests/polly/tests/presigning.rs +++ b/aws/sdk/integration-tests/polly/tests/presigning.rs @@ -5,35 +5,32 @@ use aws_sdk_polly as polly; use polly::config::{Config, Credentials, Region}; -use polly::operation::synthesize_speech::SynthesizeSpeechInput; use polly::presigning::PresigningConfig; use polly::types::{OutputFormat, VoiceId}; -use std::error::Error; use std::time::{Duration, SystemTime}; #[tokio::test] -async fn test_presigning() -> Result<(), Box> { +async fn test_presigning() { let config = Config::builder() .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .build(); + let client = polly::Client::from_conf(config); - let input = SynthesizeSpeechInput::builder() + let presigned = client + .synthesize_speech() .output_format(OutputFormat::Mp3) .text("hello, world") .voice_id(VoiceId::Joanna) - .build()?; - - let presigned = input .presigned( - &config, PresigningConfig::builder() .start_time(SystemTime::UNIX_EPOCH + Duration::from_secs(1234567891)) .expires_in(Duration::from_secs(30)) .build() .unwrap(), ) - .await?; + .await + .expect("success"); let pq = presigned.uri().path_and_query().unwrap(); let path = pq.path(); @@ -59,6 +56,4 @@ async fn test_presigning() -> Result<(), Box> { &query_params ); assert!(presigned.headers().is_empty()); - - Ok(()) } diff --git a/aws/sdk/integration-tests/qldbsession/tests/integration.rs b/aws/sdk/integration-tests/qldbsession/tests/integration.rs index 680e1647d5..cd1f77cd86 100644 --- a/aws/sdk/integration-tests/qldbsession/tests/integration.rs +++ b/aws/sdk/integration-tests/qldbsession/tests/integration.rs @@ -3,18 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_http::user_agent::AwsUserAgent; use aws_sdk_qldbsession as qldbsession; use aws_smithy_client::test_connection::TestConnection; -use aws_smithy_client::Client as CoreClient; use aws_smithy_http::body::SdkBody; use http::Uri; use qldbsession::config::{Config, Credentials, Region}; -use qldbsession::middleware::DefaultMiddleware; -use qldbsession::operation::send_command::SendCommandInput; use qldbsession::types::StartSessionRequest; +use qldbsession::Client; use std::time::{Duration, UNIX_EPOCH}; -pub type Client = CoreClient; // TODO(DVR): having the full HTTP requests right in the code is a bit gross, consider something // like https://github.com/davidbarsky/sigv4/blob/master/aws-sigv4/src/lib.rs#L283-L315 to store @@ -38,30 +34,33 @@ async fn signv4_use_correct_service_name() { .status(http::StatusCode::from_u16(200).unwrap()) .body(r#"{}"#).unwrap()), ]); - - let client = Client::new(conn.clone()); let conf = Config::builder() + .http_connector(conn.clone()) .region(Region::new("us-east-1")) .credentials_provider(Credentials::for_tests()) .build(); + let client = Client::from_conf(conf); - let mut op = SendCommandInput::builder() + let _ = client + .send_command() .start_session( StartSessionRequest::builder() .ledger_name("not-real-ledger") .build(), ) - .build() - .unwrap() - .make_operation(&conf) + .customize() .await - .expect("valid operation"); - // Fix the request time and user agent so the headers are stable - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1614952162)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - let _ = client.call(op).await.expect("request should succeed"); + .expect("should be customizable") + // Fix the request time and user agent so the headers are stable + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1614952162)) + .user_agent_for_tests() + .mutate_request(|req| { + // Remove the invocation ID since the signed request above doesn't have it + req.headers_mut().remove("amz-sdk-invocation-id"); + }) + .send() + .await + .expect("request should succeed"); conn.assert_requests_match(&[]); } diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index 4961a1f1f6..74453e21e6 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -12,32 +12,36 @@ publish = false [dev-dependencies] async-std = "1.12.0" -aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } aws-config = { path = "../../build/aws-sdk/sdk/aws-config" } +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } +aws-runtime = { path = "../../build/aws-sdk/sdk/aws-runtime", features = ["test-util"] } aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3" } aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts" } -aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["rt-tokio"] } -aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } +aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util", "rt-tokio"] } +aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "wiremock"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test" } +aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["test-util"] } +aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["test-util"] } aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1" bytes-utils = "0.1.2" -fastrand = "1.8.0" +fastrand = "2.0.0" futures-util = { version = "0.3.16", default-features = false } hdrhistogram = "7.5.2" http = "0.2.3" http-body = "0.4.5" -hyper = "0.14.25" +hyper = "0.14.26" +pretty_assertions = "1.3" serde_json = "1" smol = "1.2" tempfile = "3" tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } -# If you're writing a test with this, take heed! `no-env-filter` means you'll be capturing -# logs from everything that speaks, so be specific with your asserts. -tracing-test = { version = "0.2.4", features = ["no-env-filter"] } tracing = "0.1.37" tracing-appender = "0.2.2" tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } +# If you're writing a test with this, take heed! `no-env-filter` means you'll be capturing +# logs from everything that speaks, so be specific with your asserts. +tracing-test = { version = "0.2.4", features = ["no-env-filter"] } diff --git a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs index 209403de4a..5c1d8969cd 100644 --- a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs +++ b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs @@ -12,14 +12,17 @@ use aws_sdk_s3::types::{ }; use aws_sdk_s3::{Client, Config}; use aws_smithy_async::assert_elapsed; -use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_client::never::NeverConnector; use aws_smithy_http::result::SdkError; +use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::timeout::TimeoutConfig; use std::fmt::Debug; -use std::sync::Arc; use std::time::{Duration, Instant}; +#[cfg(not(aws_sdk_middleware_mode))] +use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs; + #[derive(Debug)] struct SmolSleep; @@ -33,7 +36,11 @@ impl AsyncSleep for SmolSleep { #[test] fn test_smol_runtime_timeouts() { - if let Err(err) = smol::block_on(async { timeout_test(Arc::new(SmolSleep)).await }) { + #[cfg(not(aws_sdk_middleware_mode))] + let _guard = capture_test_logs(); + + if let Err(err) = smol::block_on(async { timeout_test(SharedAsyncSleep::new(SmolSleep)).await }) + { println!("{err}"); panic!(); } @@ -41,7 +48,10 @@ fn test_smol_runtime_timeouts() { #[test] fn test_smol_runtime_retry() { - if let Err(err) = smol::block_on(async { retry_test(Arc::new(SmolSleep)).await }) { + #[cfg(not(aws_sdk_middleware_mode))] + let _guard = capture_test_logs(); + + if let Err(err) = smol::block_on(async { retry_test(SharedAsyncSleep::new(SmolSleep)).await }) { println!("{err}"); panic!(); } @@ -58,9 +68,12 @@ impl AsyncSleep for AsyncStdSleep { #[test] fn test_async_std_runtime_timeouts() { - if let Err(err) = - async_std::task::block_on(async { timeout_test(Arc::new(AsyncStdSleep)).await }) - { + #[cfg(not(aws_sdk_middleware_mode))] + let _guard = capture_test_logs(); + + if let Err(err) = async_std::task::block_on(async { + timeout_test(SharedAsyncSleep::new(AsyncStdSleep)).await + }) { println!("{err}"); panic!(); } @@ -68,14 +81,18 @@ fn test_async_std_runtime_timeouts() { #[test] fn test_async_std_runtime_retry() { - if let Err(err) = async_std::task::block_on(async { retry_test(Arc::new(AsyncStdSleep)).await }) + #[cfg(not(aws_sdk_middleware_mode))] + let _guard = capture_test_logs(); + + if let Err(err) = + async_std::task::block_on(async { retry_test(SharedAsyncSleep::new(AsyncStdSleep)).await }) { println!("{err}"); panic!(); } } -async fn timeout_test(sleep_impl: Arc) -> Result<(), Box> { +async fn timeout_test(sleep_impl: SharedAsyncSleep) -> Result<(), Box> { let conn = NeverConnector::new(); let region = Region::from_static("us-east-2"); let timeout_config = TimeoutConfig::builder() @@ -117,20 +134,25 @@ async fn timeout_test(sleep_impl: Arc) -> Result<(), Box) -> Result<(), Box> { +async fn retry_test(sleep_impl: SharedAsyncSleep) -> Result<(), Box> { let conn = NeverConnector::new(); let conf = aws_types::SdkConfig::builder() .region(Region::new("us-east-2")) .http_connector(conn.clone()) .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) - .retry_config(RetryConfig::standard()) + .retry_config(RetryConfig::standard().with_max_attempts(3)) .timeout_config( TimeoutConfig::builder() .operation_attempt_timeout(Duration::from_secs_f64(0.1)) diff --git a/aws/sdk/integration-tests/s3/tests/checksums.rs b/aws/sdk/integration-tests/s3/tests/checksums.rs index a82aac5e0a..be9c860628 100644 --- a/aws/sdk/integration-tests/s3/tests/checksums.rs +++ b/aws/sdk/integration-tests/s3/tests/checksums.rs @@ -5,7 +5,6 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::types::ChecksumMode; use aws_sdk_s3::Client; @@ -14,10 +13,7 @@ use aws_smithy_client::test_connection::{capture_request, TestConnection}; use aws_smithy_http::body::SdkBody; use http::header::AUTHORIZATION; use http::{HeaderValue, Uri}; -use std::{ - convert::Infallible, - time::{Duration, UNIX_EPOCH}, -}; +use std::time::{Duration, UNIX_EPOCH}; use tracing_test::traced_test; /// Test connection for the movies IT @@ -63,6 +59,7 @@ async fn test_checksum_on_streaming_response( ); let sdk_config = SdkConfig::builder() .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .time_source(UNIX_EPOCH + Duration::from_secs(1624036048)) .region(Region::new("us-east-1")) .http_connector(conn.clone()) .build(); @@ -77,14 +74,7 @@ async fn test_checksum_on_streaming_response( .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .user_agent_for_tests() .send() .await .unwrap(); @@ -191,14 +181,8 @@ async fn test_checksum_on_streaming_request<'a>( .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/config-override.rs b/aws/sdk/integration-tests/s3/tests/config-override.rs new file mode 100644 index 0000000000..620b6405fa --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/config-override.rs @@ -0,0 +1,121 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_credential_types::provider::SharedCredentialsProvider; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::Client; +use aws_smithy_client::test_connection::{capture_request, CaptureRequestReceiver}; +use aws_types::SdkConfig; + +// TODO(enableNewSmithyRuntimeCleanup): Remove this attribute once #[cfg(aws_sdk_middleware_mode)] +// has been removed +#[allow(dead_code)] +fn test_client() -> (CaptureRequestReceiver, Client) { + let (conn, captured_request) = capture_request(None); + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .region(Region::new("us-west-2")) + .http_connector(conn) + .build(); + let client = Client::new(&sdk_config); + (captured_request, client) +} + +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn operation_overrides_force_path_style() { + let (captured_request, client) = test_client(); + let _ = client + .list_objects_v2() + .bucket("test-bucket") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::config::Config::builder().force_path_style(true)) + .send() + .await; + assert_eq!( + captured_request.expect_request().uri().to_string(), + "https://s3.us-west-2.amazonaws.com/test-bucket/?list-type=2" + ); +} + +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn operation_overrides_fips() { + let (captured_request, client) = test_client(); + let _ = client + .list_objects_v2() + .bucket("test-bucket") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::config::Config::builder().use_fips(true)) + .send() + .await; + assert_eq!( + captured_request.expect_request().uri().to_string(), + "https://test-bucket.s3-fips.us-west-2.amazonaws.com/?list-type=2" + ); +} + +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn operation_overrides_dual_stack() { + let (captured_request, client) = test_client(); + let _ = client + .list_objects_v2() + .bucket("test-bucket") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::config::Config::builder().use_dual_stack(true)) + .send() + .await; + assert_eq!( + captured_request.expect_request().uri().to_string(), + "https://test-bucket.s3.dualstack.us-west-2.amazonaws.com/?list-type=2" + ); +} + +// TODO(enableNewSmithyRuntimeCleanup): Comment in the following test once Handle is no longer +// accessed in ServiceRuntimePlugin::config. Currently, a credentials cache created for a single +// operation invocation is not picked up by an identity resolver. +/* +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn operation_overrides_credentials_provider() { + let (captured_request, client) = test_client(); + let _ = client + .list_objects_v2() + .bucket("test-bucket") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::config::Config::builder().credentials_provider(Credentials::new( + "test", + "test", + Some("test".into()), + Some(std::time::UNIX_EPOCH + std::time::Duration::from_secs(1669257290 + 3600)), + "test", + ))) + .request_time_for_tests(std::time::UNIX_EPOCH + std::time::Duration::from_secs(1669257290)) + .send() + .await; + + let request = captured_request.expect_request(); + let actual_auth = + std::str::from_utf8(request.headers().get("authorization").unwrap().as_bytes()).unwrap(); + // signature would be f98cc3911dfba0daabf4343152f456bff9ecd3888a3068a1346d26949cb8f9e5 + // if we used `Credentials::for_tests()` + let expected_sig = "Signature=d7e7be63efc37c5bab5eda121999cd1c9a95efdde0cc1ce7c1b8761051cc3cbd"; + assert!( + actual_auth.contains(expected_sig), + "authorization header signature did not match expected signature: expected {} but not found in {}", + expected_sig, + actual_auth, + ); +} +*/ diff --git a/aws/sdk/integration-tests/s3/tests/config_to_builder.rs b/aws/sdk/integration-tests/s3/tests/config_to_builder.rs new file mode 100644 index 0000000000..af899ee2f6 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/config_to_builder.rs @@ -0,0 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn test_config_to_builder() { + use aws_sdk_s3::config::AppName; + + let config = aws_config::load_from_env().await; + let config = aws_sdk_s3::Config::new(&config); + // should not panic + let _ = config + .to_builder() + .app_name(AppName::new("SomeAppName").unwrap()) + .build(); +} diff --git a/aws/sdk/integration-tests/s3/tests/customizable-operation.rs b/aws/sdk/integration-tests/s3/tests/customizable-operation.rs index 24b53523a9..09dd42fb8f 100644 --- a/aws/sdk/integration-tests/s3/tests/customizable-operation.rs +++ b/aws/sdk/integration-tests/s3/tests/customizable-operation.rs @@ -5,12 +5,10 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::test_connection::capture_request; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -29,14 +27,8 @@ async fn test_s3_ops_are_customizable() { .customize() .await .expect("list_buckets is customizable") - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::<_, Infallible>::Ok(op) - }) - .expect("inserting into the property bag is infallible"); + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests(); // The response from the fake connection won't return the expected XML but we don't care about // that error in this test diff --git a/aws/sdk/integration-tests/s3/tests/data/request-information-headers/slow-network-and-late-client-clock.json b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/slow-network-and-late-client-clock.json new file mode 100644 index 0000000000..692e5d23f2 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/slow-network-and-late-client-clock.json @@ -0,0 +1,105 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20210618T170728Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "9X5E7C9EAB6AQEP2" + ], + "x-amz-id-2": [ + "gZsrBxajPyo1Q0DE2plGf7T6kAnxd4Xx7/S+8lq18GegL6kFbnVXLLh1LnBzpEpFiHN9XoNHkeA=" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Wed, 26 Apr 2023 14:00:24 GMT" + ], + "x-amz-bucket-region": [ + "us-east-1" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n\n test-bucket\n prefix~\n 1\n 1000\n false\n \n some-file.file\n 2009-10-12T17:50:30.000Z\n 434234\n STANDARD\n \n" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "One SDK operation invocation. Client retries 3 times, successful response on 3rd attempt. Slow network, one way latency is 2 seconds. Server takes 1 second to generate response. Client clock is 10 minutes behind server clock. One second delay between retries.", + "version": "V0" +} diff --git a/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-retries_and-then-success.json b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-retries_and-then-success.json new file mode 100644 index 0000000000..2903739925 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-retries_and-then-success.json @@ -0,0 +1,306 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20190601/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=43970cfd0324cb28a86459789b7a1c7684cf54b0b3c9842a84f3b24343fa038a" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20190601T000000Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "amz-sdk-request": [ + "attempt=1; max=3" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 500, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "foo-id" + ], + "x-amz-id-2": [ + "foo-id" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Sat, 01 Jun 2019 00:00:00 GMT" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n \"\n Server\n InternalError\n We encountered an internal error. Please try again.\n foo-id\n\"" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 1, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20190601/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=6d0f0da831a7d3ad1bde4e98580177bc0ef0acc21064dd26394006006392cb14" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20190601T000001Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "amz-sdk-request": [ + "ttl=20190601T000011Z; attempt=2; max=3" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 1, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 1, + "action": { + "Response": { + "response": { + "Ok": { + "status": 500, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "foo-id" + ], + "x-amz-id-2": [ + "foo-id" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Sat, 01 Jun 2019 00:00:01 GMT" + ] + } + } + } + } + } + }, + { + "connection_id": 1, + "action": { + "Data": { + "data": { + "Utf8": "\n \"\n Server\n InternalError\n We encountered an internal error. Please try again.\n foo-id\n\"" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 1, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 2, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20190601/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=8160b1d1200c10cde681ac6f4490c98023af9c4b3b8fd8a82e7560f87c126a53" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20190601T000002Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "amz-sdk-request": [ + "ttl=20190601T000012Z; attempt=3; max=3" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 2, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 2, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "9X5E7C9EAB6AQEP2" + ], + "x-amz-id-2": [ + "gZsrBxajPyo1Q0DE2plGf7T6kAnxd4Xx7/S+8lq18GegL6kFbnVXLLh1LnBzpEpFiHN9XoNHkeA=" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Sat, 01 Jun 2019 00:00:02 GMT" + ], + "x-amz-bucket-region": [ + "us-east-1" + ] + } + } + } + } + } + }, + { + "connection_id": 2, + "action": { + "Data": { + "data": { + "Utf8": "\n\n test-bucket\n prefix~\n 1\n 1000\n false\n \n some-file.file\n 2009-10-12T17:50:30.000Z\n 434234\n STANDARD\n \n" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 2, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "One SDK operation invocation. Client retries 3 times, successful response on 3rd attempt. Fast network, latency + server time is less than one second. No clock skew. Client waits 1 second between retry attempts.", + "version": "V0" +} diff --git a/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-successful-attempts.json b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-successful-attempts.json new file mode 100644 index 0000000000..180b54c5d7 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-successful-attempts.json @@ -0,0 +1,105 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20210618T170728Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "9X5E7C9EAB6AQEP2" + ], + "x-amz-id-2": [ + "gZsrBxajPyo1Q0DE2plGf7T6kAnxd4Xx7/S+8lq18GegL6kFbnVXLLh1LnBzpEpFiHN9XoNHkeA=" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Wed, 26 Apr 2023 14:00:24 GMT" + ], + "x-amz-bucket-region": [ + "us-east-1" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n\n test-bucket\n prefix~\n 1\n 1000\n false\n \n some-file.file\n 2009-10-12T17:50:30.000Z\n 434234\n STANDARD\n \n" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "Client makes 3 separate SDK operation invocations; All succeed on first attempt. Fast network, latency + server time is less than one second.", + "version": "V0" +} diff --git a/aws/sdk/integration-tests/s3/tests/endpoints.rs b/aws/sdk/integration-tests/s3/tests/endpoints.rs index 357c142900..0479f94bc5 100644 --- a/aws/sdk/integration-tests/s3/tests/endpoints.rs +++ b/aws/sdk/integration-tests/s3/tests/endpoints.rs @@ -9,7 +9,6 @@ use aws_sdk_s3::config::Builder; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::test_connection::{capture_request, CaptureRequestReceiver}; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; fn test_client(update_builder: fn(Builder) -> Builder) -> (CaptureRequestReceiver, Client) { @@ -63,6 +62,29 @@ async fn dual_stack() { ); } +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn multi_region_access_points() { + let (_captured_request, client) = test_client(|b| b); + let response = client + .get_object() + .bucket("arn:aws:s3::123456789012:accesspoint/mfzwi23gnjvgw.mrap") + .key("blah") + .send() + .await; + let error = response.expect_err("should fail—sigv4a is not supported"); + assert!( + dbg!(format!( + "{}", + aws_smithy_types::error::display::DisplayErrorContext(&error) + )) + .contains("selected auth scheme / endpoint config mismatch"), + "message should contain the correct error, found: {:?}", + error + ); +} + +#[cfg(aws_sdk_middleware_mode)] #[tokio::test] async fn multi_region_access_points() { let (_captured_request, client) = test_client(|b| b); @@ -90,12 +112,7 @@ async fn s3_object_lambda() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1234567890)); - Result::<_, Infallible>::Ok(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1234567890)) .send() .await .unwrap(); @@ -135,3 +152,22 @@ async fn s3_object_lambda_no_cross_region() { err ); } + +#[tokio::test] +async fn write_get_object_response() { + let (req, client) = test_client(|b| b); + let _write = client + .write_get_object_response() + .request_route("req-route") + .request_token("token") + .status_code(200) + .body(vec![1, 2, 3].into()) + .send() + .await; + + let captured_request = req.expect_request(); + assert_eq!( + captured_request.uri().to_string(), + "https://req-route.s3-object-lambda.us-west-4.amazonaws.com/WriteGetObjectResponse?x-id=WriteGetObjectResponse" + ); +} diff --git a/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs b/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs index f4e8704360..3e55ffe9c3 100644 --- a/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs +++ b/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs @@ -4,16 +4,12 @@ */ use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::{config::Credentials, config::Region, types::ObjectAttributes, Client}; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; use aws_types::SdkConfig; use http::header::AUTHORIZATION; -use std::{ - convert::Infallible, - time::{Duration, UNIX_EPOCH}, -}; +use std::time::{Duration, UNIX_EPOCH}; const RESPONSE_BODY_XML: &[u8] = b"\ne1AsOh9IyGCa4hLN+2Od7jlnP14="; @@ -60,14 +56,8 @@ async fn ignore_invalid_xml_body_root() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/interceptors.rs b/aws/sdk/integration-tests/s3/tests/interceptors.rs new file mode 100644 index 0000000000..be3caa493a --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/interceptors.rs @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(not(aws_sdk_middleware_mode))] +mod tests { + use aws_sdk_s3::config::interceptors::BeforeTransmitInterceptorContextMut; + use aws_sdk_s3::config::{Credentials, Region}; + use aws_sdk_s3::Client; + use aws_smithy_client::erase::DynConnector; + use aws_smithy_client::test_connection::capture_request; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; + use http::header::USER_AGENT; + use http::HeaderValue; + + #[tokio::test] + async fn interceptor_priority() { + #[derive(Debug, Eq, PartialEq)] + struct TestValue(&'static str); + impl Storable for TestValue { + type Storer = StoreReplace; + } + + #[derive(Debug)] + struct TestInterceptor(&'static str); + impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + + fn modify_before_signing( + &self, + _context: &mut BeforeTransmitInterceptorContextMut<'_>, + _components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let mut layer = Layer::new("test"); + layer.store_put(TestValue(self.0)); + cfg.push_layer(layer); + Ok(()) + } + + fn modify_before_transmit( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let value = cfg.load::().unwrap(); + context + .request_mut() + .headers_mut() + .insert("test-header", HeaderValue::from_static(value.0)); + Ok(()) + } + } + + let (conn, rx) = capture_request(None); + + // The first `TestInterceptor` will put `value1` into config + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn)) + .interceptor(TestInterceptor("value1")) + .build(); + let client = Client::from_conf(config); + + // The second `TestInterceptor` will replace `value1` with `value2` in config + dbg!( + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .customize() + .await + .unwrap() + .interceptor(TestInterceptor("value2")) + .send() + .await + ) + .expect_err("no fake response set"); + + let request = rx.expect_request(); + assert_eq!("value2", request.headers()["test-header"]); + } + + #[tokio::test] + async fn set_test_user_agent_through_request_mutation() { + let (conn, rx) = capture_request(None); + + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn.clone())) + .build(); + let client = Client::from_conf(config); + + dbg!( + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .customize() + .await + .unwrap() + .mutate_request(|request| { + let headers = request.headers_mut(); + headers.insert(USER_AGENT, HeaderValue::try_from("test").unwrap()); + headers.insert("x-amz-user-agent", HeaderValue::try_from("test").unwrap()); + }) + .send() + .await + ) + .expect_err("no fake response set"); + + let request = rx.expect_request(); + assert_eq!("test", request.headers()[USER_AGENT]); + assert_eq!("test", request.headers()["x-amz-user-agent"]); + } +} diff --git a/aws/sdk/integration-tests/s3/tests/make-connector-override.rs b/aws/sdk/integration-tests/s3/tests/make-connector-override.rs index fc816b8188..90eb79d70c 100644 --- a/aws/sdk/integration-tests/s3/tests/make-connector-override.rs +++ b/aws/sdk/integration-tests/s3/tests/make-connector-override.rs @@ -5,7 +5,7 @@ use aws_credential_types::provider::SharedCredentialsProvider; use aws_credential_types::Credentials; -use aws_smithy_async::rt::sleep::{AsyncSleep, TokioSleep}; +use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_client::http_connector::{ConnectorSettings, HttpConnector}; use aws_smithy_client::test_connection; @@ -25,7 +25,7 @@ async fn make_connector_fn_test() { let sentinel = Arc::new(AtomicUsize::new(0)); let connector_sentinel = sentinel.clone(); let connector_with_counter = HttpConnector::ConnectorFn(Arc::new( - move |_settings: &ConnectorSettings, _sleep: Option>| { + move |_settings: &ConnectorSettings, _sleep: Option| { connector_sentinel.fetch_add(1, Ordering::Relaxed); Some(test_connection::infallible_connection_fn(|_req| { http::Response::builder().status(200).body("ok!").unwrap() @@ -60,7 +60,7 @@ async fn timeouts_can_be_set_by_service() { let sdk_config = SdkConfig::builder() .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) .region(Region::from_static("us-east-1")) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .timeout_config( TimeoutConfig::builder() .operation_timeout(Duration::from_secs(5)) diff --git a/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs b/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs index eef5b0b14b..96142f2b43 100644 --- a/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs +++ b/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs @@ -4,15 +4,11 @@ */ use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::{config::Credentials, config::Region, primitives::ByteStream, Client}; use aws_smithy_client::test_connection::capture_request; use aws_types::SdkConfig; use http::HeaderValue; -use std::{ - convert::Infallible, - time::{Duration, UNIX_EPOCH}, -}; +use std::time::{Duration, UNIX_EPOCH}; const NAUGHTY_STRINGS: &str = include_str!("blns/blns.txt"); @@ -83,14 +79,8 @@ async fn test_s3_signer_with_naughty_string_metadata() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs b/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs index 1f903abb7a..a50867ccd0 100644 --- a/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs +++ b/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs @@ -5,11 +5,9 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::primitives::ByteStream; use aws_sdk_s3::{config::Credentials, config::Region, Client}; use aws_smithy_client::test_connection::capture_request; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -33,14 +31,8 @@ async fn test_operation_should_not_normalize_uri_path() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1669257290)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1669257290)) + .user_agent_for_tests() .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/presigning.rs b/aws/sdk/integration-tests/s3/tests/presigning.rs index 6f8a33d571..05625d3405 100644 --- a/aws/sdk/integration-tests/s3/tests/presigning.rs +++ b/aws/sdk/integration-tests/s3/tests/presigning.rs @@ -4,47 +4,73 @@ */ use aws_sdk_s3 as s3; +use futures_util::future::FutureExt; +use futures_util::Future; use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http::{HeaderMap, HeaderValue}; use s3::config::{Credentials, Region}; -use s3::operation::get_object::GetObjectInput; -use s3::operation::head_object::HeadObjectInput; -use s3::operation::put_object::PutObjectInput; -use s3::operation::upload_part::UploadPartInput; +use s3::operation::get_object::builders::GetObjectFluentBuilder; +use s3::operation::head_object::builders::HeadObjectFluentBuilder; +use s3::operation::put_object::builders::PutObjectFluentBuilder; +use s3::operation::upload_part::builders::UploadPartFluentBuilder; use s3::presigning::{PresignedRequest, PresigningConfig}; -use std::error::Error; +use std::pin::Pin; use std::time::{Duration, SystemTime}; +trait TestOperation { + fn presign_for_test( + self, + config: PresigningConfig, + ) -> Pin>>; +} + +macro_rules! rig_operation { + ($fluent_builder:ident) => { + impl TestOperation for $fluent_builder { + fn presign_for_test( + self, + config: PresigningConfig, + ) -> Pin>> { + Box::pin($fluent_builder::presigned(self, config).map(|out| out.expect("success"))) + } + } + }; +} + +rig_operation!(GetObjectFluentBuilder); +rig_operation!(PutObjectFluentBuilder); +rig_operation!(UploadPartFluentBuilder); +rig_operation!(HeadObjectFluentBuilder); + /// Generates a `PresignedRequest` from the given input. /// Assumes that that input has a `presigned` method on it. -macro_rules! presign_input { - ($input:expr) => {{ - let creds = Credentials::for_tests(); - let config = s3::Config::builder() - .credentials_provider(creds) - .region(Region::new("us-east-1")) - .build(); - - let req: PresignedRequest = $input - .presigned( - &config, - PresigningConfig::builder() - .start_time(SystemTime::UNIX_EPOCH + Duration::from_secs(1234567891)) - .expires_in(Duration::from_secs(30)) - .build() - .unwrap(), - ) - .await?; - req - }}; +async fn presign(operation: O) -> PresignedRequest +where + O: FnOnce(s3::Client) -> F, + F: TestOperation, +{ + let creds = Credentials::for_tests(); + let config = s3::Config::builder() + .credentials_provider(creds) + .region(Region::new("us-east-1")) + .build(); + let client = s3::Client::from_conf(config); + + operation(client) + .presign_for_test( + PresigningConfig::builder() + .start_time(SystemTime::UNIX_EPOCH + Duration::from_secs(1234567891)) + .expires_in(Duration::from_secs(30)) + .build() + .unwrap(), + ) + .await } #[tokio::test] -async fn test_presigning() -> Result<(), Box> { - let presigned = presign_input!(GetObjectInput::builder() - .bucket("test-bucket") - .key("test-key") - .build()?); +async fn test_presigning() { + let presigned = + presign(|client| client.get_object().bucket("test-bucket").key("test-key")).await; let pq = presigned.uri().path_and_query().unwrap(); let path = pq.path(); @@ -52,13 +78,13 @@ async fn test_presigning() -> Result<(), Box> { let mut query_params: Vec<&str> = query.split('&').collect(); query_params.sort(); - assert_eq!( + pretty_assertions::assert_eq!( "test-bucket.s3.us-east-1.amazonaws.com", presigned.uri().authority().unwrap() ); assert_eq!("GET", presigned.method().as_str()); assert_eq!("/test-key", path); - assert_eq!( + pretty_assertions::assert_eq!( &[ "X-Amz-Algorithm=AWS4-HMAC-SHA256", "X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request", @@ -72,18 +98,19 @@ async fn test_presigning() -> Result<(), Box> { &query_params ); assert!(presigned.headers().is_empty()); - - Ok(()) } #[tokio::test] -async fn test_presigning_with_payload_headers() -> Result<(), Box> { - let presigned = presign_input!(PutObjectInput::builder() - .bucket("test-bucket") - .key("test-key") - .content_length(12345) - .content_type("application/x-test") - .build()?); +async fn test_presigning_with_payload_headers() { + let presigned = presign(|client| { + client + .put_object() + .bucket("test-bucket") + .key("test-key") + .content_length(12345) + .content_type("application/x-test") + }) + .await; let pq = presigned.uri().path_and_query().unwrap(); let path = pq.path(); @@ -91,13 +118,13 @@ async fn test_presigning_with_payload_headers() -> Result<(), Box> { let mut query_params: Vec<&str> = query.split('&').collect(); query_params.sort(); - assert_eq!( + pretty_assertions::assert_eq!( "test-bucket.s3.us-east-1.amazonaws.com", presigned.uri().authority().unwrap() ); assert_eq!("PUT", presigned.method().as_str()); assert_eq!("/test-key", path); - assert_eq!( + pretty_assertions::assert_eq!( &[ "X-Amz-Algorithm=AWS4-HMAC-SHA256", "X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request", @@ -115,49 +142,49 @@ async fn test_presigning_with_payload_headers() -> Result<(), Box> { expected_headers.insert(CONTENT_LENGTH, HeaderValue::from_static("12345")); expected_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/x-test")); assert_eq!(&expected_headers, presigned.headers()); - - Ok(()) } #[tokio::test] -async fn test_presigned_upload_part() -> Result<(), Box> { - let presigned = presign_input!(UploadPartInput::builder() - .content_length(12345) - .bucket("bucket") - .key("key") - .part_number(0) - .upload_id("upload-id") - .build()?); - assert_eq!( - presigned.uri().to_string(), +async fn test_presigned_upload_part() { + let presigned = presign(|client| { + client + .upload_part() + .content_length(12345) + .bucket("bucket") + .key("key") + .part_number(0) + .upload_id("upload-id") + }) + .await; + pretty_assertions::assert_eq!( "https://bucket.s3.us-east-1.amazonaws.com/key?x-id=UploadPart&partNumber=0&uploadId=upload-id&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=content-length%3Bhost&X-Amz-Signature=a702867244f0bd1fb4d161e2a062520dcbefae3b9992d2e5366bcd61a60c6ddd&X-Amz-Security-Token=notarealsessiontoken", + presigned.uri().to_string(), ); - Ok(()) } #[tokio::test] -async fn test_presigning_object_lambda() -> Result<(), Box> { - let presigned = presign_input!(GetObjectInput::builder() - .bucket("arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:my-banner-ap-name") - .key("test2.txt") - .build() - .unwrap()); +async fn test_presigning_object_lambda() { + let presigned = presign(|client| { + client + .get_object() + .bucket("arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:my-banner-ap-name") + .key("test2.txt") + }) + .await; // since the URI is `my-banner-api-name...` we know EP2 is working properly for presigning - assert_eq!(presigned.uri().to_string(), "https://my-banner-ap-name-123456789012.s3-object-lambda.us-west-2.amazonaws.com/test2.txt?x-id=GetObject&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-west-2%2Fs3-object-lambda%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=027976453050b6f9cca7af80a59c05ee572b462e0fc1ef564c59412b903fcdf2&X-Amz-Security-Token=notarealsessiontoken"); - Ok(()) + pretty_assertions::assert_eq!( + "https://my-banner-ap-name-123456789012.s3-object-lambda.us-west-2.amazonaws.com/test2.txt?x-id=GetObject&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-west-2%2Fs3-object-lambda%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=027976453050b6f9cca7af80a59c05ee572b462e0fc1ef564c59412b903fcdf2&X-Amz-Security-Token=notarealsessiontoken", + presigned.uri().to_string() + ); } #[tokio::test] -async fn test_presigned_head_object() -> Result<(), Box> { - let presigned = presign_input!(HeadObjectInput::builder() - .bucket("bucket") - .key("key") - .build()?); +async fn test_presigned_head_object() { + let presigned = presign(|client| client.head_object().bucket("bucket").key("key")).await; assert_eq!("HEAD", presigned.method().as_str()); - assert_eq!( - presigned.uri().to_string(), + pretty_assertions::assert_eq!( "https://bucket.s3.us-east-1.amazonaws.com/key?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=6b97012e70d5ee3528b5591e0e90c0f45e0fa303506f854eff50ff922751a193&X-Amz-Security-Token=notarealsessiontoken", + presigned.uri().to_string(), ); - Ok(()) } diff --git a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs index 03393a5a74..7d7880fa81 100644 --- a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs +++ b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs @@ -5,11 +5,9 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::test_connection::capture_request; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -35,14 +33,8 @@ async fn test_s3_signer_query_string_with_all_valid_chars() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await; diff --git a/aws/sdk/integration-tests/s3/tests/reconnects.rs b/aws/sdk/integration-tests/s3/tests/reconnects.rs index 85afcd40a9..cb29bb2a66 100644 --- a/aws/sdk/integration-tests/s3/tests/reconnects.rs +++ b/aws/sdk/integration-tests/s3/tests/reconnects.rs @@ -3,21 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_credential_types::provider::SharedCredentialsProvider; -use aws_credential_types::Credentials; +use aws_sdk_s3::config::retry::{ReconnectMode, RetryConfig}; +use aws_sdk_s3::config::{Credentials, Region, SharedAsyncSleep}; use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_client::test_connection::wire_mock::{ check_matches, ReplayedEvent, WireLevelTestConnection, }; use aws_smithy_client::{ev, match_events}; -use aws_smithy_types::retry::{ReconnectMode, RetryConfig}; -use aws_types::region::Region; -use aws_types::SdkConfig; -use std::sync::Arc; #[tokio::test] -/// test that disabling reconnects on retry config disables them for the client -async fn disable_reconnects() { +async fn test_disable_reconnect_on_503() { let mock = WireLevelTestConnection::spinup(vec![ ReplayedEvent::status(503), ReplayedEvent::status(503), @@ -25,17 +20,17 @@ async fn disable_reconnects() { ]) .await; - let sdk_config = SdkConfig::builder() + let config = aws_sdk_s3::Config::builder() .region(Region::from_static("us-east-2")) - .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) - .sleep_impl(Arc::new(TokioSleep::new())) + .credentials_provider(Credentials::for_tests()) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .endpoint_url(mock.endpoint_url()) .http_connector(mock.http_connector()) .retry_config( RetryConfig::standard().with_reconnect_mode(ReconnectMode::ReuseAllConnections), ) .build(); - let client = aws_sdk_s3::Client::new(&sdk_config); + let client = aws_sdk_s3::Client::from_conf(config); let resp = client .get_object() .bucket("bucket") @@ -57,7 +52,7 @@ async fn disable_reconnects() { } #[tokio::test] -async fn reconnect_on_503() { +async fn test_enabling_reconnect_on_503() { let mock = WireLevelTestConnection::spinup(vec![ ReplayedEvent::status(503), ReplayedEvent::status(503), @@ -65,15 +60,17 @@ async fn reconnect_on_503() { ]) .await; - let sdk_config = SdkConfig::builder() + let config = aws_sdk_s3::Config::builder() .region(Region::from_static("us-east-2")) - .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) - .sleep_impl(Arc::new(TokioSleep::new())) + .credentials_provider(Credentials::for_tests()) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .endpoint_url(mock.endpoint_url()) .http_connector(mock.http_connector()) - .retry_config(RetryConfig::standard()) + .retry_config( + RetryConfig::standard().with_reconnect_mode(ReconnectMode::ReconnectOnTransientError), + ) .build(); - let client = aws_sdk_s3::Client::new(&sdk_config); + let client = aws_sdk_s3::Client::from_conf(config); let resp = client .get_object() .bucket("bucket") diff --git a/aws/sdk/integration-tests/s3/tests/request_id.rs b/aws/sdk/integration-tests/s3/tests/request_id.rs index 67d9523fb5..d81da66b42 100644 --- a/aws/sdk/integration-tests/s3/tests/request_id.rs +++ b/aws/sdk/integration-tests/s3/tests/request_id.rs @@ -3,34 +3,48 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::operation::get_object::{GetObject, GetObjectError}; -use aws_sdk_s3::operation::list_buckets::ListBuckets; +use aws_sdk_s3::operation::get_object::GetObjectError; use aws_sdk_s3::operation::{RequestId, RequestIdExt}; +use aws_sdk_s3::{config::Credentials, config::Region, Client, Config}; +use aws_smithy_client::test_connection::capture_request; use aws_smithy_http::body::SdkBody; -use aws_smithy_http::operation; -use aws_smithy_http::response::ParseHttpResponse; -use bytes::Bytes; -#[test] -fn get_request_id_from_modeled_error() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(404) - .body( - r#" - - NoSuchKey - The resource you requested does not exist - /mybucket/myfoto.jpg - incorrect-request-id - "#, - ) - .unwrap(); - let err = GetObject::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect_err("status was 404, this is an error"); - assert!(matches!(err, GetObjectError::NoSuchKey(_))); +#[tokio::test] +async fn get_request_id_from_modeled_error() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(404) + .body(SdkBody::from( + r#" + + NoSuchKey + The resource you requested does not exist + /mybucket/myfoto.jpg + incorrect-request-id + "#, + )) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let err = client + .get_object() + .key("dontcare") + .send() + .await + .expect_err("status was 404, this is an error") + .into_service_error(); + request.expect_request(); + assert!( + matches!(err, GetObjectError::NoSuchKey(_)), + "expected NoSuchKey, got {err:?}", + ); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); assert_eq!( @@ -43,25 +57,38 @@ fn get_request_id_from_modeled_error() { ); } -#[test] -fn get_request_id_from_unmodeled_error() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(500) - .body( - r#" - - SomeUnmodeledError - Something bad happened - /mybucket/myfoto.jpg - incorrect-request-id - "#, - ) - .unwrap(); - let err = GetObject::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect_err("status 500"); +#[tokio::test] +async fn get_request_id_from_unmodeled_error() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(500) + .body(SdkBody::from( + r#" + + SomeUnmodeledError + Something bad happened + /mybucket/myfoto.jpg + incorrect-request-id + "#, + )) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let err = client + .get_object() + .key("dontcare") + .send() + .await + .expect_err("status 500") + .into_service_error(); + request.expect_request(); assert!(matches!(err, GetObjectError::Unhandled(_))); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); @@ -75,23 +102,34 @@ fn get_request_id_from_unmodeled_error() { ); } -#[test] -fn get_request_id_from_successful_nonstreaming_response() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(200) - .body( - r#" - - some-idsome-display-name - - "#, - ) - .unwrap(); - let output = ListBuckets::new() - .parse_loaded(&resp.map(Bytes::from)) +#[tokio::test] +async fn get_request_id_from_successful_nonstreaming_response() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(200) + .body(SdkBody::from( + r#" + + some-idsome-display-name + + "#, + )) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let output = client + .list_buckets() + .send() + .await .expect("valid successful response"); + request.expect_request(); assert_eq!(Some("correct-request-id"), output.request_id()); assert_eq!( Some("correct-extended-request-id"), @@ -99,18 +137,29 @@ fn get_request_id_from_successful_nonstreaming_response() { ); } -#[test] -fn get_request_id_from_successful_streaming_response() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(200) - .body(SdkBody::from("some streaming file data")) - .unwrap(); - let mut resp = operation::Response::new(resp); - let output = GetObject::new() - .parse_unloaded(&mut resp) +#[tokio::test] +async fn get_request_id_from_successful_streaming_response() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(200) + .body(SdkBody::from("some streaming file data")) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let output = client + .get_object() + .key("dontcare") + .send() + .await .expect("valid successful response"); + request.expect_request(); assert_eq!(Some("correct-request-id"), output.request_id()); assert_eq!( Some("correct-extended-request-id"), @@ -119,26 +168,37 @@ fn get_request_id_from_successful_streaming_response() { } // Verify that the conversion from operation error to the top-level service error maintains the request ID -#[test] -fn conversion_to_service_error_maintains_request_id() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(404) - .body( - r#" - - NoSuchKey - The resource you requested does not exist - /mybucket/myfoto.jpg - incorrect-request-id - "#, - ) - .unwrap(); - let err = GetObject::new() - .parse_loaded(&resp.map(Bytes::from)) +#[tokio::test] +async fn conversion_to_service_error_maintains_request_id() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(404) + .body(SdkBody::from( + r#" + + NoSuchKey + The resource you requested does not exist + /mybucket/myfoto.jpg + incorrect-request-id + "#, + )) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let err = client + .get_object() + .key("dontcare") + .send() + .await .expect_err("status was 404, this is an error"); - + request.expect_request(); let service_error: aws_sdk_s3::Error = err.into(); assert_eq!(Some("correct-request-id"), service_error.request_id()); assert_eq!( diff --git a/aws/sdk/integration-tests/s3/tests/request_information_headers.rs b/aws/sdk/integration-tests/s3/tests/request_information_headers.rs new file mode 100644 index 0000000000..33ee18fb9d --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/request_information_headers.rs @@ -0,0 +1,285 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(not(aws_sdk_middleware_mode))] +mod tests { + use aws_http::user_agent::AwsUserAgent; + use aws_runtime::invocation_id::{InvocationId, PredefinedInvocationIdGenerator}; + use aws_sdk_s3::config::interceptors::BeforeSerializationInterceptorContextMut; + use aws_sdk_s3::config::interceptors::FinalizerInterceptorContextRef; + use aws_sdk_s3::config::retry::RetryConfig; + use aws_sdk_s3::config::timeout::TimeoutConfig; + use aws_sdk_s3::config::{Credentials, Region}; + use aws_sdk_s3::config::{Interceptor, SharedAsyncSleep}; + use aws_sdk_s3::Client; + use aws_smithy_async::test_util::InstantSleep; + use aws_smithy_async::test_util::ManualTimeSource; + use aws_smithy_async::time::SharedTimeSource; + use aws_smithy_client::dvr; + use aws_smithy_client::dvr::MediaType; + use aws_smithy_client::erase::DynConnector; + use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + use std::time::{Duration, UNIX_EPOCH}; + + // # One SDK operation invocation. + // # Client retries 3 times, successful response on 3rd attempt. + // # Fast network, latency + server time is less than one second. + // # No clock skew + // # Client waits 1 second between retry attempts. + #[tokio::test] + async fn three_retries_and_then_success() { + let _logs = capture_test_logs(); + + #[derive(Debug)] + struct TimeInterceptor { + time_source: ManualTimeSource, + } + impl Interceptor for TimeInterceptor { + fn name(&self) -> &'static str { + "TimeInterceptor" + } + + fn modify_before_serialization( + &self, + _context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let mut layer = Layer::new("test"); + layer.store_put(AwsUserAgent::for_tests()); + cfg.push_layer(layer); + Ok(()) + } + + fn read_after_attempt( + &self, + _context: &FinalizerInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.time_source.advance(Duration::from_secs(1)); + tracing::info!( + "################ ADVANCED TIME BY 1 SECOND, {:?}", + &self.time_source + ); + Ok(()) + } + } + + let time_source = ManualTimeSource::new(UNIX_EPOCH + Duration::from_secs(1559347200)); + + let path = "tests/data/request-information-headers/three-retries_and-then-success.json"; + let conn = dvr::ReplayingConnection::from_file(path).unwrap(); + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn.clone())) + .time_source(SharedTimeSource::new(time_source.clone())) + .sleep_impl(SharedAsyncSleep::new(InstantSleep::new(Default::default()))) + .retry_config(RetryConfig::standard()) + .timeout_config( + TimeoutConfig::builder() + .connect_timeout(Duration::from_secs(10)) + .read_timeout(Duration::from_secs(10)) + .build(), + ) + .invocation_id_generator(PredefinedInvocationIdGenerator::new(vec![ + InvocationId::new_from_str("00000000-0000-4000-8000-000000000000"), + ])) + .interceptor(TimeInterceptor { time_source }) + .build(); + let client = Client::from_conf(config); + + let resp = dbg!( + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .send() + .await + ); + + let resp = resp.expect("valid e2e test"); + assert_eq!(resp.name(), Some("test-bucket")); + conn.full_validate(MediaType::Xml).await.expect("failed") + } + // + // // # Client makes 3 separate SDK operation invocations + // // # All succeed on first attempt. + // // # Fast network, latency + server time is less than one second. + // // - request: + // // time: 2019-06-01T00:00:00Z + // // headers: + // // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 + // // amz-sdk-request: attempt=1; max=3 + // // response: + // // status: 200 + // // time_received: 2019-06-01T00:00:00Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:00:00 GMT + // // - request: + // // time: 2019-06-01T00:01:01Z + // // headers: + // // # Note the different invocation id because it's a new SDK + // // # invocation operation. + // // amz-sdk-invocation-id: 70370531-7b83-4b90-8b93-46975687ecf6 + // // amz-sdk-request: ttl=20190601T000011Z; attempt=1; max=3 + // // response: + // // status: 200 + // // time_received: 2019-06-01T00:00:01Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:00:01 GMT + // // - request: + // // time: 2019-06-01T00:00:02Z + // // headers: + // // amz-sdk-invocation-id: 910bf450-6c90-43de-a508-3fa126a06b71 + // // amz-sdk-request: ttl=20190601T000012Z; attempt=1; max=3 + // // response: + // // status: 200 + // // time_received: 2019-06-01T00:00:02Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:00:02 GMT + // const THREE_SUCCESSFUL_ATTEMPTS_PATH: &str = "test-data/request-information-headers/three-successful-attempts.json"; + // #[tokio::test] + // async fn three_successful_attempts() { + // tracing_subscriber::fmt::init(); + // + // impl RuntimePlugin for FixupPlugin { + // fn configure( + // &self, + // cfg: &mut ConfigBag, + // ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { + // let params_builder = Params::builder() + // .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) + // .bucket("test-bucket"); + // + // cfg.put(params_builder); + // cfg.set_request_time(RequestTime::new(self.timestamp.clone())); + // cfg.put(AwsUserAgent::for_tests()); + // cfg.put(InvocationId::for_tests()); + // Ok(()) + // } + // } + // + // let conn = dvr::ReplayingConnection::from_file(THREE_SUCCESSFUL_ATTEMPTS_PATH).unwrap(); + // let config = aws_sdk_s3::Config::builder() + // .credentials_provider(Credentials::for_tests()) + // .region(Region::new("us-east-1")) + // .http_connector(DynConnector::new(conn.clone())) + // .build(); + // let client = Client::from_conf(config); + // let fixup = FixupPlugin { + // client: client.clone(), + // timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), + // }; + // + // let resp = dbg!( + // client + // .list_objects_v2() + // .bucket("test-bucket") + // .prefix("prefix~") + // .send_v2_with_plugin(Some(fixup)) + // .await + // ); + // + // let resp = resp.expect("valid e2e test"); + // assert_eq!(resp.name(), Some("test-bucket")); + // conn.full_validate(MediaType::Xml).await.expect("failed") + // } + // + // // # One SDK operation invocation. + // // # Client retries 3 times, successful response on 3rd attempt. + // // # Slow network, one way latency is 2 seconds. + // // # Server takes 1 second to generate response. + // // # Client clock is 10 minutes behind server clock. + // // # One second delay between retries. + // // - request: + // // time: 2019-06-01T00:00:00Z + // // headers: + // // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 + // // amz-sdk-request: attempt=1; max=3 + // // response: + // // status: 500 + // // time_received: 2019-06-01T00:00:05Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:10:03 GMT + // // - request: + // // time: 2019-06-01T00:00:06Z + // // # The ttl is 00:00:16 with the client clock, + // // # but accounting for skew we have + // // # 00:10:03 - 00:00:05 = 00:09:58 + // // # ttl = 00:00:16 + 00:09:58 = 00:10:14 + // // headers: + // // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 + // // amz-sdk-request: ttl=20190601T001014Z; attempt=2; max=3 + // // response: + // // status: 500 + // // time_received: 2019-06-01T00:00:11Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:10:09 GMT + // // - request: + // // time: 2019-06-01T00:00:12Z + // // headers: + // // # ttl = 00:00:12 + 20 = 00:00:22 + // // # skew is: + // // # 00:10:09 - 00:00:11 + // // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 + // // amz-sdk-request: ttl=20190601T001020Z; attempt=3; max=3 + // // response: + // // status: 200 + // // time_received: 2019-06-01T00:00:17Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:10:15 GMT + // const SLOW_NETWORK_AND_LATE_CLIENT_CLOCK_PATH: &str = "test-data/request-information-headers/slow-network-and-late-client-clock.json"; + // #[tokio::test] + // async fn slow_network_and_late_client_clock() { + // tracing_subscriber::fmt::init(); + // + // impl RuntimePlugin for FixupPlugin { + // fn configure( + // &self, + // cfg: &mut ConfigBag, + // ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { + // let params_builder = Params::builder() + // .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) + // .bucket("test-bucket"); + // + // cfg.put(params_builder); + // cfg.set_request_time(RequestTime::new(self.timestamp.clone())); + // cfg.put(AwsUserAgent::for_tests()); + // cfg.put(InvocationId::for_tests()); + // Ok(()) + // } + // } + // + // let conn = dvr::ReplayingConnection::from_file(SLOW_NETWORK_AND_LATE_CLIENT_CLOCK_PATH).unwrap(); + // let config = aws_sdk_s3::Config::builder() + // .credentials_provider(Credentials::for_tests()) + // .region(Region::new("us-east-1")) + // .http_connector(DynConnector::new(conn.clone())) + // .build(); + // let client = Client::from_conf(config); + // let fixup = FixupPlugin { + // client: client.clone(), + // timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), + // }; + // + // let resp = dbg!( + // client + // .list_objects_v2() + // .bucket("test-bucket") + // .prefix("prefix~") + // .send_v2_with_plugin(Some(fixup)) + // .await + // ); + // + // let resp = resp.expect("valid e2e test"); + // assert_eq!(resp.name(), Some("test-bucket")); + // conn.full_validate(MediaType::Xml).await.expect("failed") + // } +} diff --git a/aws/sdk/integration-tests/s3/tests/required-query-params.rs b/aws/sdk/integration-tests/s3/tests/required-query-params.rs index 7cf6238da0..b5fede83a0 100644 --- a/aws/sdk/integration-tests/s3/tests/required-query-params.rs +++ b/aws/sdk/integration-tests/s3/tests/required-query-params.rs @@ -3,48 +3,60 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::config::Region; -use aws_sdk_s3::operation::abort_multipart_upload::AbortMultipartUploadInput; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::error::DisplayErrorContext; +use aws_sdk_s3::Client; +use aws_smithy_client::test_connection::capture_request; use aws_smithy_http::operation::error::BuildError; #[tokio::test] async fn test_error_when_required_query_param_is_unset() { - let conf = aws_sdk_s3::Config::builder() + let (conn, _request) = capture_request(None); + let config = aws_sdk_s3::Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .build(); + let client = Client::from_conf(config); - let err = AbortMultipartUploadInput::builder() + let err = client + .abort_multipart_upload() .bucket("test-bucket") .key("test.txt") - .build() - .unwrap() - .make_operation(&conf) + .send() .await .unwrap_err(); - - assert_eq!( - BuildError::missing_field("upload_id", "cannot be empty or unset").to_string(), - err.to_string(), + let expected = BuildError::missing_field("upload_id", "cannot be empty or unset").to_string(); + let actual = format!("{}", DisplayErrorContext(err)); + assert!( + actual.contains(&expected), + "expected error to contain '{expected}', but was '{actual}'", ) } #[tokio::test] async fn test_error_when_required_query_param_is_set_but_empty() { - let conf = aws_sdk_s3::Config::builder() + let (conn, _request) = capture_request(None); + let config = aws_sdk_s3::Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .build(); - let err = AbortMultipartUploadInput::builder() + let client = Client::from_conf(config); + + let err = client + .abort_multipart_upload() .bucket("test-bucket") .key("test.txt") .upload_id("") - .build() - .unwrap() - .make_operation(&conf) + .send() .await .unwrap_err(); - assert_eq!( - BuildError::missing_field("upload_id", "cannot be empty or unset").to_string(), - err.to_string(), + let expected = BuildError::missing_field("upload_id", "cannot be empty or unset").to_string(); + let actual = format!("{}", DisplayErrorContext(err)); + assert!( + actual.contains(&expected), + "expected error to contain '{expected}', but was '{actual}'", ) } diff --git a/aws/sdk/integration-tests/s3/tests/signing-it.rs b/aws/sdk/integration-tests/s3/tests/signing-it.rs index 6e20e187e2..a02b6b670b 100644 --- a/aws/sdk/integration-tests/s3/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3/tests/signing-it.rs @@ -5,12 +5,10 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -36,14 +34,8 @@ async fn test_signer() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await; diff --git a/aws/sdk/integration-tests/s3/tests/sleep_impl_use_cases.rs b/aws/sdk/integration-tests/s3/tests/sleep_impl_use_cases.rs index 182214e477..dfa82e9660 100644 --- a/aws/sdk/integration-tests/s3/tests/sleep_impl_use_cases.rs +++ b/aws/sdk/integration-tests/s3/tests/sleep_impl_use_cases.rs @@ -8,7 +8,7 @@ mod with_sdk_config { use aws_config::timeout::TimeoutConfig; use aws_config::SdkConfig; use aws_sdk_s3 as s3; - use std::sync::Arc; + use aws_smithy_async::rt::sleep::SharedAsyncSleep; use std::time::Duration; #[tokio::test] @@ -101,7 +101,9 @@ mod with_sdk_config { .build(), ) .retry_config(RetryConfig::standard().with_max_attempts(2)) - .sleep_impl(Arc::new(aws_smithy_async::rt::sleep::TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new( + aws_smithy_async::rt::sleep::TokioSleep::new(), + )) .build(); assert!(config.timeout_config().unwrap().has_timeouts()); assert!(config.retry_config().unwrap().has_retry()); @@ -114,7 +116,7 @@ mod with_service_config { use aws_config::timeout::TimeoutConfig; use aws_config::SdkConfig; use aws_sdk_s3 as s3; - use std::sync::Arc; + use aws_smithy_async::rt::sleep::SharedAsyncSleep; use std::time::Duration; #[test] @@ -188,7 +190,9 @@ mod with_service_config { .build(), ) .retry_config(RetryConfig::standard().with_max_attempts(2)) - .sleep_impl(Arc::new(aws_smithy_async::rt::sleep::TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new( + aws_smithy_async::rt::sleep::TokioSleep::new(), + )) .build(); let _s3 = s3::Client::new(&config); } diff --git a/aws/sdk/integration-tests/s3/tests/timeouts.rs b/aws/sdk/integration-tests/s3/tests/timeouts.rs index e4d28b72f4..59359ad20b 100644 --- a/aws/sdk/integration-tests/s3/tests/timeouts.rs +++ b/aws/sdk/integration-tests/s3/tests/timeouts.rs @@ -12,13 +12,12 @@ use aws_sdk_s3::types::{ }; use aws_sdk_s3::Client; use aws_smithy_async::assert_elapsed; -use aws_smithy_async::rt::sleep::{default_async_sleep, TokioSleep}; +use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep, TokioSleep}; use aws_smithy_client::never::NeverConnector; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::timeout::TimeoutConfig; use std::future::Future; use std::net::SocketAddr; -use std::sync::Arc; use std::time::Duration; use tokio::net::TcpListener; use tokio::time::timeout; @@ -34,7 +33,7 @@ async fn test_timeout_service_ends_request_that_never_completes() { .operation_timeout(Duration::from_secs_f32(0.5)) .build(), ) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(); let client = Client::new(&sdk_config); @@ -65,7 +64,12 @@ async fn test_timeout_service_ends_request_that_never_completes() { .await .unwrap_err(); - assert_eq!("TimeoutError(TimeoutError { source: RequestTimeoutError { kind: \"operation timeout (all attempts including retries)\", duration: 500ms } })", format!("{:?}", err)); + let expected = "operation timeout (all attempts including retries) occurred after 500ms"; + let message = format!("{}", DisplayErrorContext(err)); + assert!( + message.contains(expected), + "expected '{message}' to contain '{expected}'" + ); assert_elapsed!(now, std::time::Duration::from_secs_f32(0.5)); } diff --git a/aws/sdk/integration-tests/s3control/tests/signing-it.rs b/aws/sdk/integration-tests/s3control/tests/signing-it.rs index f5c583bbcd..c868491a42 100644 --- a/aws/sdk/integration-tests/s3control/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3control/tests/signing-it.rs @@ -4,13 +4,11 @@ */ use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3control::config::{Credentials, Region}; use aws_sdk_s3control::Client; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; use aws_types::SdkConfig; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -39,14 +37,9 @@ async fn test_signer() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1636751225)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1636751225)) + .user_agent_for_tests() + .remove_invocation_id_for_tests() .send() .await .expect_err("empty response"); diff --git a/aws/sdk/integration-tests/sts/tests/signing-it.rs b/aws/sdk/integration-tests/sts/tests/signing-it.rs index f2c80bd9f7..74e82e0026 100644 --- a/aws/sdk/integration-tests/sts/tests/signing-it.rs +++ b/aws/sdk/integration-tests/sts/tests/signing-it.rs @@ -24,6 +24,8 @@ async fn assume_role_signed() { ); } +// TODO(enableNewSmithyRuntimeCleanup): Delete the middleware version of this test +#[cfg(aws_sdk_middleware_mode)] #[tokio::test] async fn web_identity_unsigned() { let creds = Credentials::for_tests(); @@ -42,6 +44,23 @@ async fn web_identity_unsigned() { ); } +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn web_identity_unsigned() { + let (server, request) = capture_request(None); + let conf = aws_sdk_sts::Config::builder() + .region(Region::new("us-east-1")) + .http_connector(server) + .build(); + let client = aws_sdk_sts::Client::from_conf(conf); + let _ = client.assume_role_with_web_identity().send().await; + // web identity should be unsigned + assert_eq!( + request.expect_request().headers().get("AUTHORIZATION"), + None + ); +} + #[tokio::test] async fn assume_role_saml_unsigned() { let (server, request) = capture_request(None); diff --git a/aws/sdk/integration-tests/timestreamquery/Cargo.toml b/aws/sdk/integration-tests/timestreamquery/Cargo.toml new file mode 100644 index 0000000000..99955e4764 --- /dev/null +++ b/aws/sdk/integration-tests/timestreamquery/Cargo.toml @@ -0,0 +1,21 @@ +# This Cargo.toml is unused in generated code. It exists solely to enable these tests to compile in-situ +[package] +name = "timestream-tests" +version = "0.1.0" +authors = ["Russell Cohen "] +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dev-dependencies] +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } +aws-sdk-timestreamquery = { path = "../../build/aws-sdk/sdk/timestreamquery" } +aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] } +aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"] } +aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["test-util"] } +aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } +tokio = { version = "1.23.1", features = ["full", "test-util"] } +tracing-subscriber = "0.3.17" diff --git a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs new file mode 100644 index 0000000000..16d4222515 --- /dev/null +++ b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs @@ -0,0 +1,78 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn do_endpoint_discovery() { + use aws_credential_types::provider::SharedCredentialsProvider; + use aws_sdk_timestreamquery as query; + use aws_sdk_timestreamquery::config::Credentials; + use aws_smithy_async::rt::sleep::SharedAsyncSleep; + use aws_smithy_async::test_util::controlled_time_and_sleep; + use aws_smithy_async::time::{SharedTimeSource, TimeSource}; + use aws_smithy_client::dvr::{MediaType, ReplayingConnection}; + use aws_types::region::Region; + use aws_types::SdkConfig; + use std::time::{Duration, UNIX_EPOCH}; + + let _logs = aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs(); + + let conn = ReplayingConnection::from_file("tests/traffic.json").unwrap(); + //let conn = aws_smithy_client::dvr::RecordingConnection::new(conn); + let start = UNIX_EPOCH + Duration::from_secs(1234567890); + let (ts, sleep, mut gate) = controlled_time_and_sleep(start); + let config = SdkConfig::builder() + .http_connector(conn.clone()) + .region(Region::from_static("us-west-2")) + .sleep_impl(SharedAsyncSleep::new(sleep)) + .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .time_source(SharedTimeSource::new(ts.clone())) + .build(); + let conf = query::config::Builder::from(&config) + .idempotency_token_provider("0000-0000-0000") + .build(); + let (client, reloader) = query::Client::from_conf(conf) + .with_endpoint_discovery_enabled() + .await + .expect("initial setup of endpoint discovery failed"); + + tokio::spawn(reloader.reload_task()); + + let _resp = client + .query() + .query_string("SELECT now() as time_now") + .send() + .await + .unwrap(); + + // wait 10 minutes for the endpoint to expire + while ts.now() < start + Duration::from_secs(60 * 10) { + assert_eq!( + gate.expect_sleep().await.duration(), + Duration::from_secs(60) + ); + } + + // the recording validates that this request hits another endpoint + let _resp = client + .query() + .query_string("SELECT now() as time_now") + .send() + .await + .unwrap(); + // if you want to update this test: + // conn.dump_to_file("tests/traffic.json").unwrap(); + conn.validate_body_and_headers( + Some(&[ + "x-amz-security-token", + "x-amz-date", + "content-type", + "x-amz-target", + ]), + MediaType::Json, + ) + .await + .unwrap(); +} diff --git a/aws/sdk/integration-tests/timestreamquery/tests/traffic.json b/aws/sdk/integration-tests/timestreamquery/tests/traffic.json new file mode 100644 index 0000000000..6b692ce1dc --- /dev/null +++ b/aws/sdk/integration-tests/timestreamquery/tests/traffic.json @@ -0,0 +1,407 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://query.timestream.us-west-2.amazonaws.com/", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head api/timestreamquery/0.0.0-local os/macos lang/rust/1.67.1" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20090213/us-west-2/timestream/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, Signature=1b50e2545f06c8e1ca0e205c20f25a34b6aab82f3a47e4cc370e9a5fea01d08c" + ], + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "x-amz-target": [ + "Timestream_20181101.DescribeEndpoints" + ], + "user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head os/macos lang/rust/1.67.1" + ], + "x-amz-date": [ + "20090213T233130Z" + ] + }, + "method": "POST" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "{}" + }, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amzn-requestid": [ + "fcfdab03-2bb8-45e9-a284-b789cf7efb63" + ], + "content-type": [ + "application/x-amz-json-1.0" + ], + "date": [ + "Wed, 24 May 2023 15:51:07 GMT" + ], + "content-length": [ + "104" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "{\"Endpoints\":[{\"Address\":\"query-cell1.timestream.us-west-2.amazonaws.com\",\"CachePeriodInMinutes\":10}]}" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 1, + "action": { + "Request": { + "request": { + "uri": "https://query-cell1.timestream.us-west-2.amazonaws.com/", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "content-length": [ + "73" + ], + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head os/macos lang/rust/1.67.1" + ], + "x-amz-date": [ + "20090213T233130Z" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20090213/us-west-2/timestream/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, Signature=8174b6ca0ece22834b562b60785f47ef354b2c1ddf7a541482f255006b5f98c2" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head api/timestreamquery/0.0.0-local os/macos lang/rust/1.67.1" + ], + "x-amz-target": [ + "Timestream_20181101.Query" + ] + }, + "method": "POST" + } + } + } + }, + { + "connection_id": 1, + "action": { + "Data": { + "data": { + "Utf8": "{\"QueryString\":\"SELECT now() as time_now\",\"ClientToken\":\"0000-0000-0000\"}" + }, + "direction": "Request" + } + } + }, + { + "connection_id": 1, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 1, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "date": [ + "Wed, 24 May 2023 15:51:08 GMT" + ], + "x-amzn-requestid": [ + "OFO47BQ6XAXGTIK3XH4S6GBFLQ" + ], + "content-length": [ + "318" + ] + } + } + } + } + } + }, + { + "connection_id": 1, + "action": { + "Data": { + "data": { + "Utf8": "{\"ColumnInfo\":[{\"Name\":\"time_now\",\"Type\":{\"ScalarType\":\"TIMESTAMP\"}}],\"QueryId\":\"AEDACANMQDLSTQR3SPDV6HEWYACX5IALTEWGK7JM3VSFQQ5J5F3HGKVEMNHBRHY\",\"QueryStatus\":{\"CumulativeBytesMetered\":10000000,\"CumulativeBytesScanned\":0,\"ProgressPercentage\":100.0},\"Rows\":[{\"Data\":[{\"ScalarValue\":\"2023-05-24 15:51:08.760000000\"}]}]}" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 1, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 2, + "action": { + "Request": { + "request": { + "uri": "https://query.timestream.us-west-2.amazonaws.com/", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head os/macos lang/rust/1.67.1" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head api/timestreamquery/0.0.0-local os/macos lang/rust/1.67.1" + ], + "x-amz-date": [ + "20090213T234030Z" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20090213/us-west-2/timestream/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, Signature=32e468e574c514ba0fa4a0f0304cef82b72f82965b9e8792fd63f6efb67b297c" + ], + "x-amz-target": [ + "Timestream_20181101.DescribeEndpoints" + ], + "x-amz-security-token": [ + "notarealsessiontoken" + ] + }, + "method": "POST" + } + } + } + }, + { + "connection_id": 2, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "content-length": [ + "104" + ], + "date": [ + "Wed, 24 May 2023 15:51:07 GMT" + ], + "x-amzn-requestid": [ + "fcfdab03-2bb8-45e9-a284-b789cf7efb63" + ] + } + } + } + } + } + }, + { + "connection_id": 2, + "action": { + "Data": { + "data": { + "Utf8": "{}" + }, + "direction": "Request" + } + } + }, + { + "connection_id": 2, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 2, + "action": { + "Data": { + "data": { + "Utf8": "{\"Endpoints\":[{\"Address\":\"query-cell2.timestream.us-west-2.amazonaws.com\",\"CachePeriodInMinutes\":10}]}" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 2, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 3, + "action": { + "Request": { + "request": { + "uri": "https://query-cell2.timestream.us-west-2.amazonaws.com/", + "headers": { + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20090213/us-west-2/timestream/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, Signature=8b2abd7688aefb4004200e5fa087f6c154fde879f2df79d3f6c57934cdc8f62a" + ], + "x-amz-date": [ + "20090213T234130Z" + ], + "user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head os/macos lang/rust/1.67.1" + ], + "x-amz-target": [ + "Timestream_20181101.Query" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head api/timestreamquery/0.0.0-local os/macos lang/rust/1.67.1" + ], + "content-type": [ + "application/x-amz-json-1.0" + ], + "content-length": [ + "73" + ], + "x-amz-security-token": [ + "notarealsessiontoken" + ] + }, + "method": "POST" + } + } + } + }, + { + "connection_id": 3, + "action": { + "Data": { + "data": { + "Utf8": "{\"QueryString\":\"SELECT now() as time_now\",\"ClientToken\":\"0000-0000-0000\"}" + }, + "direction": "Request" + } + } + }, + { + "connection_id": 3, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "date": [ + "Wed, 24 May 2023 15:51:08 GMT" + ], + "x-amzn-requestid": [ + "OFO47BQ6XAXGTIK3XH4S6GBFLQ" + ], + "content-length": [ + "318" + ] + } + } + } + } + } + }, + { + "connection_id": 3, + "action": { + "Data": { + "data": { + "Utf8": "{\"ColumnInfo\":[{\"Name\":\"time_now\",\"Type\":{\"ScalarType\":\"TIMESTAMP\"}}],\"QueryId\":\"AEDACANMQDLSTQR3SPDV6HEWYACX5IALTEWGK7JM3VSFQQ5J5F3HGKVEMNHBRHY\",\"QueryStatus\":{\"CumulativeBytesMetered\":10000000,\"CumulativeBytesScanned\":0,\"ProgressPercentage\":100.0},\"Rows\":[{\"Data\":[{\"ScalarValue\":\"2023-05-24 15:51:08.760000000\"}]}]}" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 3, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "todo docs", + "version": "V0" +} diff --git a/aws/sdk/integration-tests/transcribestreaming/Cargo.toml b/aws/sdk/integration-tests/transcribestreaming/Cargo.toml index 38b1018cc5..181ba493cb 100644 --- a/aws/sdk/integration-tests/transcribestreaming/Cargo.toml +++ b/aws/sdk/integration-tests/transcribestreaming/Cargo.toml @@ -22,4 +22,5 @@ hound = "3.4.0" http = "0.2.0" serde_json = "1.0.0" tokio = { version = "1.23.1", features = ["full", "test-util"] } +tracing = "0.1" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/Cargo.toml b/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/Cargo.toml deleted file mode 100644 index 3642d7ba24..0000000000 --- a/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "using-native-tls-instead-of-rustls" -version = "0.1.0" -authors = ["AWS Rust SDK Team "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dev-dependencies] -# aws-config pulls in rustls and several other things by default. We have to disable defaults in order to use native-tls -# and then manually bring the other defaults back -aws-config = { path = "../../build/aws-sdk/sdk/aws-config", default-features = false, features = [ - "native-tls", - "rt-tokio", -] } -# aws-sdk-s3 brings in rustls by default so we disable that in order to use native-tls only -aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3", default-features = false, features = [ - "native-tls", -] } -tokio = { version = "1.20.1", features = ["rt", "macros"] } diff --git a/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/tests/no-rustls-in-dependency.rs b/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/tests/no-rustls-in-dependency.rs deleted file mode 100644 index 1df97865db..0000000000 --- a/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/tests/no-rustls-in-dependency.rs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -/// The SDK defaults to using RusTLS by default but you can also use [`native_tls`](https://github.com/sfackler/rust-native-tls) -/// which will choose a TLS implementation appropriate for your platform. This test looks much like -/// any other. Activating and deactivating `features` in your app's `Cargo.toml` is all that's needed. - -async fn list_buckets() -> Result<(), aws_sdk_s3::Error> { - let sdk_config = aws_config::load_from_env().await; - let client = aws_sdk_s3::Client::new(&sdk_config); - - let _resp = client.list_buckets().send().await?; - - Ok(()) -} - -/// You can run this test to ensure that it is only using `native-tls` and -/// that nothing is pulling in `rustls` as a dependency -#[test] -#[should_panic = "error: package ID specification `rustls` did not match any packages"] -fn test_rustls_is_not_in_dependency_tree() { - let cargo_location = std::env::var("CARGO").unwrap(); - let cargo_command = std::process::Command::new(cargo_location) - .arg("tree") - .arg("--invert") - .arg("rustls") - .output() - .expect("failed to run 'cargo tree'"); - - let stderr = String::from_utf8_lossy(&cargo_command.stderr); - - // We expect the call to `cargo tree` to error out. If it did, we panic with the resulting - // message here. In the case that no error message is set, that's bad. - if !stderr.is_empty() { - panic!("{}", stderr); - } - - // Uh oh. We expected an error message but got none, likely because `cargo tree` found - // `rustls` in our dependencies. We'll print out the message we got to see what went wrong. - let stdout = String::from_utf8_lossy(&cargo_command.stdout); - - println!("{}", stdout) -} - -// NOTE: not currently run in CI, separate PR will set up a with-creds CI runner -#[tokio::test] -#[ignore] -async fn needs_creds_native_tls_works() { - list_buckets().await.expect("should succeed") -} diff --git a/aws/sdk/integration-tests/webassembly/.cargo/config.toml b/aws/sdk/integration-tests/webassembly/.cargo/config.toml index 031dad7377..e6e321b297 100644 --- a/aws/sdk/integration-tests/webassembly/.cargo/config.toml +++ b/aws/sdk/integration-tests/webassembly/.cargo/config.toml @@ -3,3 +3,7 @@ target = "wasm32-wasi" [target.wasm32-wasi] rustflags = ["-C", "opt-level=1"] + +# TODO(Rust 1.70): The sparse registry config can be removed when upgrading to Rust 1.70 +[registries.crates-io] +protocol = "sparse" diff --git a/aws/sdk/sdk-external-types.toml b/aws/sdk/sdk-external-types.toml index d2ab675425..b484544c27 100644 --- a/aws/sdk/sdk-external-types.toml +++ b/aws/sdk/sdk-external-types.toml @@ -3,11 +3,13 @@ allowed_external_types = [ "aws_credential_types::*", "aws_endpoint::*", "aws_http::*", - "aws_sig_auth::*", + "aws_runtime::*", "aws_smithy_async::*", "aws_smithy_client::*", "aws_smithy_http::*", "aws_smithy_http_tower::*", + "aws_smithy_runtime::*", + "aws_smithy_runtime_api::*", "aws_smithy_types::*", "aws_types::*", "http::header::map::HeaderMap", diff --git a/aws/sra-test/.gitignore b/aws/sra-test/.gitignore deleted file mode 100644 index 388d181b4d..0000000000 --- a/aws/sra-test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/smithy-build.json diff --git a/aws/sra-test/build.gradle.kts b/aws/sra-test/build.gradle.kts deleted file mode 100644 index 7344916af0..0000000000 --- a/aws/sra-test/build.gradle.kts +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -extra["displayName"] = "Smithy :: Rust :: AWS-SDK :: SRA Test" -extra["moduleName"] = "software.amazon.smithy.rust.awssdk.sra.test" - -tasks["jar"].enabled = false - -plugins { - id("software.amazon.smithy") -} - -val smithyVersion: String by project -val defaultRustDocFlags: String by project -val properties = PropertyRetriever(rootProject, project) - -val pluginName = "rust-client-codegen" -val workingDirUnderBuildDir = "smithyprojections/sdk-sra-test/" - -val publisherToolPath = rootProject.projectDir.resolve("tools/ci-build/publisher") -val outputDir = buildDir.resolve("sdk") - -configure { - outputDirectory = file("$buildDir/$workingDirUnderBuildDir") -} - -buildscript { - val smithyVersion: String by project - dependencies { - classpath("software.amazon.smithy:smithy-cli:$smithyVersion") - } -} - -dependencies { - implementation(project(":aws:sdk-codegen")) - implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") - implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") -} - -data class Service( - val serviceId: String, - val moduleName: String, - val imports: List, -) -val servicesToGenerate = listOf( - Service( - "com.amazonaws.dynamodb#DynamoDB_20120810", - "aws-sdk-dynamodb", - listOf("../sdk/aws-models/dynamodb.json"), - ), - Service( - "com.amazonaws.s3#AmazonS3", - "aws-sdk-s3", - listOf("../sdk/aws-models/s3.json", "../sdk/aws-models/s3-tests.smithy"), - ), -) -val allCodegenTests = servicesToGenerate.map { - CodegenTest( - it.serviceId, - it.moduleName, - imports = it.imports, - extraConfig = """ - , - "codegen": { - "includeFluentClient": false, - "enableNewSmithyRuntime": true - }, - "customizationConfig": { - "awsSdk": { - "generateReadme": false - } - } - """, - ) -} - -project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests) -project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) -project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) - -tasks["smithyBuildJar"].dependsOn("generateSmithyBuild") -tasks["assemble"].finalizedBy("generateCargoWorkspace") - -project.registerModifyMtimeTask() -project.registerCargoCommandsTasks(buildDir.resolve(workingDirUnderBuildDir), defaultRustDocFlags) - -tasks["test"].finalizedBy(cargoCommands(properties).map { it.toString }) - -tasks["clean"].doFirst { delete("smithy-build.json") } - -/** - * The aws/rust-runtime crates depend on local versions of the Smithy core runtime enabling local compilation. However, - * those paths need to be replaced in the final build. We should probably fix this with some symlinking. - */ -fun rewritePathDependency(line: String): String { - // some runtime crates are actually dependent on the generated bindings: - return line.replace("../sdk/build/aws-sdk/sdk/", "") - // others use relative dependencies:: - .replace("../../rust-runtime/", "") -} - -tasks.register("relocateServices") { - description = "relocate AWS services to their final destination" - doLast { - servicesToGenerate.forEach { service -> - logger.info("Relocating ${service.moduleName}...") - copy { - from("$buildDir/smithyprojections/sdk-sra-test/${service.moduleName}/rust-client-codegen") - into(outputDir.resolve(service.moduleName)) - } - copy { - from(projectDir.resolve("integration-tests/${service.moduleName}/tests")) - into(outputDir.resolve(service.moduleName).resolve("tests")) - } - } - } - dependsOn("smithyBuildJar") - inputs.dir("$buildDir/smithyprojections/sdk-sra-test/") - outputs.dir(outputDir) -} -tasks["assemble"].apply { - dependsOn("relocateServices") -} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/.gitignore b/aws/sra-test/integration-tests/aws-sdk-s3/.gitignore deleted file mode 100644 index 5a44eef09a..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/Cargo.lock diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml deleted file mode 100644 index cdc7a56ab5..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "aws-smithy-runtime-test" -version = "0.1.0" -edition = "2021" -publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -aws-credential-types = { path = "../../../rust-runtime/aws-credential-types", features = ["test-util"] } -aws-http = { path = "../../../rust-runtime/aws-http" } -aws-sigv4 = { path = "../../../rust-runtime/aws-sigv4" } -aws-types = { path = "../../../rust-runtime/aws-types" } -aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3" } -aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["rt-tokio"] } -aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client" } -aws-smithy-types = { path = "../../../../rust-runtime/aws-smithy-types" } -aws-smithy-http = { path = "../../../../rust-runtime/aws-smithy-http" } -aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime" } -aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api", features = ["test-util"] } -tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } -tracing = "0.1.37" -tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } -http = "0.2.3" -http-body = "0.4.5" diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs deleted file mode 100644 index fd4c8649c0..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -// type TxReq = http::Request; -// type TxRes = http::Response; -// -// pub struct SigV4SigningConfigInterceptor { -// pub signing_service: &'static str, -// pub signing_region: Option, -// } - -// // Mount the interceptors -// let mut interceptors = Interceptors::new(); -// let sig_v4_signing_config_interceptor = SigV4SigningConfigInterceptor { -// signing_region: service_config.region.clone(), -// signing_service: service_config.signing_service(), -// }; -// let credentials_cache_interceptor = CredentialsCacheInterceptor { -// shared_credentials_cache: service_config.credentials_cache.clone(), -// }; -// let checksum_interceptor = ChecksumInterceptor { -// checksum_mode: input.checksum_mode().cloned(), -// }; -// interceptors -// .with_interceptor(sig_v4_signing_config_interceptor) -// .with_interceptor(credentials_cache_interceptor) -// .with_interceptor(checksum_interceptor); - -// let token_bucket = Box::new(standard::TokenBucket::builder().max_tokens(500).build()); -// -// impl Interceptor for SigV4SigningConfigInterceptor { -// fn modify_before_signing( -// &mut self, -// context: &mut InterceptorContext, -// ) -> Result<(), InterceptorError> { -// let mut props = context.properties_mut(); -// -// let mut signing_config = OperationSigningConfig::default_config(); -// signing_config.signing_options.content_sha256_header = true; -// signing_config.signing_options.double_uri_encode = false; -// signing_config.signing_options.normalize_uri_path = false; -// props.insert(signing_config); -// props.insert(aws_types::SigningService::from_static(self.signing_service)); -// -// if let Some(signing_region) = self.signing_region.as_ref() { -// props.insert(aws_types::region::SigningRegion::from( -// signing_region.clone(), -// )); -// } -// -// Ok(()) -// } -// } -// -// pub struct CredentialsCacheInterceptor { -// pub shared_credentials_cache: SharedCredentialsCache, -// } -// -// impl Interceptor for CredentialsCacheInterceptor { -// fn modify_before_signing( -// &mut self, -// context: &mut InterceptorContext, -// ) -> Result<(), InterceptorError> { -// match self -// .shared_credentials_cache -// .as_ref() -// .provide_cached_credentials() -// .now_or_never() -// { -// Some(Ok(creds)) => { -// context.properties_mut().insert(creds); -// } -// // ignore the case where there is no credentials cache wired up -// Some(Err(CredentialsError::CredentialsNotLoaded { .. })) => { -// tracing::info!("credentials cache returned CredentialsNotLoaded, ignoring") -// } -// // if we get another error class, there is probably something actually wrong that the user will -// // want to know about -// Some(Err(other)) => return Err(InterceptorError::ModifyBeforeSigning(other.into())), -// None => unreachable!("fingers crossed that creds are always available"), -// } -// -// Ok(()) -// } -// } -// -// pub struct ChecksumInterceptor { -// pub checksum_mode: Option, -// } -// -// impl Interceptor for ChecksumInterceptor { -// fn modify_before_serialization( -// &mut self, -// context: &mut InterceptorContext, -// ) -> Result<(), InterceptorError> { -// let mut props = context.properties_mut(); -// props.insert(self.checksum_mode.clone()); -// -// Ok(()) -// } -// } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs deleted file mode 100644 index caa1b5bba9..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -mod interceptors; - -#[tokio::test] -async fn sra_test() { - tracing_subscriber::fmt::init(); - - // TODO(orchestrator-testing): Replace the connector with a fake request/response - let config = aws_sdk_s3::Config::builder().build(); - let client = aws_sdk_s3::Client::from_conf(config); - - let _ = dbg!( - client - .get_object() - .bucket("zhessler-test-bucket") - .key("1000-lines.txt") - .send_v2() - .await - ); -} diff --git a/buildSrc/src/main/kotlin/CodegenTestCommon.kt b/buildSrc/src/main/kotlin/CodegenTestCommon.kt index 2173c237a6..cc996eae82 100644 --- a/buildSrc/src/main/kotlin/CodegenTestCommon.kt +++ b/buildSrc/src/main/kotlin/CodegenTestCommon.kt @@ -18,6 +18,7 @@ data class CodegenTest( val service: String, val module: String, val extraConfig: String? = null, + val extraCodegenConfig: String? = null, val imports: List = emptyList(), ) @@ -38,6 +39,7 @@ private fun generateSmithyBuild(projectDir: String, pluginName: String, tests: L "relativePath": "$projectDir/rust-runtime" }, "codegen": { + ${it.extraCodegenConfig ?: ""} }, "service": "${it.service}", "module": "${it.module}", @@ -244,25 +246,29 @@ fun Project.registerCargoCommandsTasks( this.tasks.register(Cargo.CHECK.toString) { dependsOn(dependentTasks) workingDir(outputDir) + environment("RUSTFLAGS", "--cfg aws_sdk_unstable") commandLine("cargo", "check", "--lib", "--tests", "--benches", "--all-features") } this.tasks.register(Cargo.TEST.toString) { dependsOn(dependentTasks) workingDir(outputDir) - commandLine("cargo", "test", "--all-features") + environment("RUSTFLAGS", "--cfg aws_sdk_unstable") + commandLine("cargo", "test", "--all-features", "--no-fail-fast") } this.tasks.register(Cargo.DOCS.toString) { dependsOn(dependentTasks) workingDir(outputDir) environment("RUSTDOCFLAGS", defaultRustDocFlags) + environment("RUSTFLAGS", "--cfg aws_sdk_unstable") commandLine("cargo", "doc", "--no-deps", "--document-private-items") } this.tasks.register(Cargo.CLIPPY.toString) { dependsOn(dependentTasks) workingDir(outputDir) + environment("RUSTFLAGS", "--cfg aws_sdk_unstable") commandLine("cargo", "clippy") } } diff --git a/buildSrc/src/main/kotlin/CrateSet.kt b/buildSrc/src/main/kotlin/CrateSet.kt index 47ff406b71..132246cf39 100644 --- a/buildSrc/src/main/kotlin/CrateSet.kt +++ b/buildSrc/src/main/kotlin/CrateSet.kt @@ -10,6 +10,8 @@ object CrateSet { "aws-endpoint", "aws-http", "aws-hyper", + "aws-runtime", + "aws-runtime-api", "aws-sig-auth", "aws-sigv4", "aws-types", @@ -38,6 +40,7 @@ object CrateSet { val SERVER_SMITHY_RUNTIME = SMITHY_RUNTIME_COMMON + listOf( "aws-smithy-http-server", "aws-smithy-http-server-python", + "aws-smithy-http-server-typescript", ) val ENTIRE_SMITHY_RUNTIME = (AWS_SDK_SMITHY_RUNTIME + SERVER_SMITHY_RUNTIME).toSortedSet() diff --git a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt index 44510d1b11..189bce6a67 100644 --- a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt +++ b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt @@ -19,6 +19,38 @@ data class RootTest( val manifestName: String, ) +// TODO(https://github.com/awslabs/smithy-rs/issues/2810): We can remove the `Flat` layout after the switch +// to `Workspaces` has been released. This can be checked by looking at the `examples/` directory in aws-sdk-rust's +// main branch. +// +// The `Flat` layout is retained for backwards compatibility so that the next release process can succeed. +enum class AwsExamplesLayout { + /** + * Directory layout for examples used prior to June 26, 2023, + * where each example was in the `rust_dev_preview/` root directory and + * was considered to be its own workspace. + * + * This layout had issues with CI in terms of time to compile and disk space required + * since the dependencies would get recompiled for every example. + */ + Flat, + + /** + * Current directory layout where there are a small number of workspaces + * rooted in `rust_dev_preview/`. + */ + Workspaces, + ; + + companion object { + fun detect(project: Project): AwsExamplesLayout = if (project.projectDir.resolve("examples/Cargo.toml").exists()) { + AwsExamplesLayout.Flat + } else { + AwsExamplesLayout.Workspaces + } + } +} + class AwsServices( private val project: Project, services: List, @@ -36,18 +68,24 @@ class AwsServices( ( services.map(AwsService::module).map { "sdk/$it" } + CrateSet.AWS_SDK_SMITHY_RUNTIME.map { "sdk/$it" } + - CrateSet.AWS_SDK_RUNTIME.map { "sdk/$it" } + - examples + CrateSet.AWS_SDK_RUNTIME.map { "sdk/$it" } // Root tests should not be included since they can't be part of the root Cargo workspace - // in order to test differences in Cargo features. + // in order to test differences in Cargo features. Examples should not be included either + // because each example itself is a workspace. ).toSortedSet() } val examples: List by lazy { - project.projectDir.resolve("examples") - .listFiles { file -> !file.name.startsWith(".") }.orEmpty().toList() - .filter { file -> manifestCompatibleWithGeneratedServices(file) } - .map { "examples/${it.name}" } + val examplesRoot = project.projectDir.resolve("examples") + if (AwsExamplesLayout.detect(project) == AwsExamplesLayout.Flat) { + examplesRoot.listFiles { file -> !file.name.startsWith(".") }.orEmpty().toList() + .filter { file -> manifestCompatibleWithGeneratedServices(file) } + .map { "examples/${it.name}" } + } else { + examplesRoot.listFiles { file -> + !file.name.startsWith(".") && file.isDirectory() && file.resolve("Cargo.toml").exists() + }.orEmpty().toList().map { "examples/${it.name}" } + } } /** @@ -78,6 +116,16 @@ class AwsServices( false } } + + /** + * Returns a sorted set of members included in the workspace. + */ + fun includedInWorkspace() = allModules + + /** + * Returns a list of crates excluded from the workspace. + */ + fun excludedFromWorkspace() = examples + rootTests.map(RootTest::manifestName) } /** @@ -197,7 +245,7 @@ fun parseMembership(rawList: String): Membership { val inclusions = mutableSetOf() val exclusions = mutableSetOf() - rawList.split(",").map { it.trim() }.forEach { item -> + rawList.split(",").map { it.trim() }.filter { it.isNotEmpty() }.forEach { item -> when { item.startsWith('-') -> exclusions.add(item.substring(1)) item.startsWith('+') -> inclusions.add(item.substring(1)) diff --git a/ci.mk b/ci.mk index c30de0d113..a7022355e6 100644 --- a/ci.mk +++ b/ci.mk @@ -52,6 +52,10 @@ check-aws-sdk-smoketest-unit-tests: generate-aws-sdk-smoketest check-aws-sdk-standalone-integration-tests: generate-aws-sdk-smoketest $(CI_ACTION) $@ $(ARGS) +.PHONY: check-book +check-book: check-rust-runtimes + $(CI_ACTION) $@ $(ARGS) + .PHONY: check-client-codegen-integration-tests check-client-codegen-integration-tests: $(CI_ACTION) $@ $(ARGS) @@ -120,6 +124,10 @@ generate-codegen-diff: check-deterministic-codegen: $(CI_ACTION) $@ $(ARGS) +.PHONY: check-semver +check-semver: + $(CI_ACTION) $@ $(ARGS) + .PHONY: generate-smithy-rs-release generate-smithy-rs-release: $(CI_ACTION) $@ $(ARGS) diff --git a/clippy-root.toml b/clippy-root.toml new file mode 100644 index 0000000000..87318f3163 --- /dev/null +++ b/clippy-root.toml @@ -0,0 +1,9 @@ +# this file is named `clippy-root.toml` so it isn't picked up automagically. Clippy +# will search up the filesystem for a clippy.toml and this causes problems with tools. +disallowed-methods = [ + # fully qualified function/method name: + "std::time::SystemTime::now", + "std::time::SystemTime::elapsed", + "std::time::Instant::now", + "std::time::Instant::elapsed" +] diff --git a/codegen-client-test/build.gradle.kts b/codegen-client-test/build.gradle.kts index f3796a6911..ae84542a3e 100644 --- a/codegen-client-test/build.gradle.kts +++ b/codegen-client-test/build.gradle.kts @@ -15,6 +15,7 @@ plugins { val smithyVersion: String by project val defaultRustDocFlags: String by project val properties = PropertyRetriever(rootProject, project) +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "orchestrator" val pluginName = "rust-client-codegen" val workingDirUnderBuildDir = "smithyprojections/codegen-client-test/" @@ -33,75 +34,88 @@ dependencies { implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") } -val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> - listOf( - CodegenTest("com.amazonaws.simple#SimpleService", "simple", imports = listOf("$commonModels/simple.smithy")), - CodegenTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"), - CodegenTest("com.amazonaws.ebs#Ebs", "ebs", imports = listOf("$commonModels/ebs.json")), - CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), - CodegenTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), - CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), - CodegenTest("aws.protocoltests.restjson#RestJsonExtras", "rest_json_extras", imports = listOf("$commonModels/rest-json-extras.smithy")), - CodegenTest("aws.protocoltests.misc#MiscService", "misc", imports = listOf("$commonModels/misc.smithy")), - CodegenTest( - "aws.protocoltests.restxml#RestXml", "rest_xml", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - - CodegenTest( - "aws.protocoltests.query#AwsQuery", "aws_query", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "aws.protocoltests.ec2#AwsEc2", "ec2_query", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "aws.protocoltests.restxml.xmlns#RestXmlWithNamespace", - "rest_xml_namespace", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "aws.protocoltests.restxml#RestXmlExtras", - "rest_xml_extras", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "aws.protocoltests.restxmlunwrapped#RestXmlExtrasUnwrappedErrors", - "rest_xml_extras_unwrapped", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "crate#Config", - "naming_test_ops", - """ - , "codegen": { "renameErrors": false } - """.trimIndent(), - imports = listOf("$commonModels/naming-obstacle-course-ops.smithy"), - ), - CodegenTest( - "casing#ACRONYMInside_Service", - "naming_test_casing", - imports = listOf("$commonModels/naming-obstacle-course-casing.smithy"), - ), - CodegenTest( - "naming_obs_structs#NamingObstacleCourseStructs", - "naming_test_structs", - """ - , "codegen": { "renameErrors": false } - """.trimIndent(), - imports = listOf("$commonModels/naming-obstacle-course-structs.smithy"), - ), - CodegenTest("aws.protocoltests.json#TestService", "endpoint-rules"), - CodegenTest("com.aws.example.rust#PokemonService", "pokemon-service-client", imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy")), - CodegenTest("com.aws.example.rust#PokemonService", "pokemon-service-awsjson-client", imports = listOf("$commonModels/pokemon-awsjson.smithy", "$commonModels/pokemon-common.smithy")), +data class ClientTest( + val serviceShapeName: String, + val moduleName: String, + val dependsOn: List = emptyList(), + val addMessageToErrors: Boolean = true, + val renameErrors: Boolean = true, +) { + fun toCodegenTest(): CodegenTest = CodegenTest( + serviceShapeName, + moduleName, + extraCodegenConfig = extraCodegenConfig(), + imports = imports(), ) + + private fun extraCodegenConfig(): String = StringBuilder().apply { + append("\"addMessageToErrors\": $addMessageToErrors,\n") + append("\"renameErrors\": $renameErrors\n,") + append("\"enableNewSmithyRuntime\": \"${getSmithyRuntimeMode()}\"") + }.toString() + + private fun imports(): List = dependsOn.map { "../codegen-core/common-test-models/$it" } } +val allCodegenTests = listOf( + ClientTest("com.amazonaws.simple#SimpleService", "simple", dependsOn = listOf("simple.smithy")), + ClientTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"), + ClientTest("com.amazonaws.ebs#Ebs", "ebs", dependsOn = listOf("ebs.json")), + ClientTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), + ClientTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), + ClientTest("aws.protocoltests.restjson#RestJson", "rest_json"), + ClientTest( + "aws.protocoltests.restjson#RestJsonExtras", + "rest_json_extras", + dependsOn = listOf("rest-json-extras.smithy"), + ), + ClientTest("aws.protocoltests.misc#MiscService", "misc", dependsOn = listOf("misc.smithy")), + ClientTest("aws.protocoltests.restxml#RestXml", "rest_xml", addMessageToErrors = false), + ClientTest("aws.protocoltests.query#AwsQuery", "aws_query", addMessageToErrors = false), + ClientTest("aws.protocoltests.ec2#AwsEc2", "ec2_query", addMessageToErrors = false), + ClientTest("aws.protocoltests.restxml.xmlns#RestXmlWithNamespace", "rest_xml_namespace", addMessageToErrors = false), + ClientTest("aws.protocoltests.restxml#RestXmlExtras", "rest_xml_extras", addMessageToErrors = false), + ClientTest( + "aws.protocoltests.restxmlunwrapped#RestXmlExtrasUnwrappedErrors", + "rest_xml_extras_unwrapped", + addMessageToErrors = false, + ), + ClientTest( + "crate#Config", + "naming_test_ops", + dependsOn = listOf("naming-obstacle-course-ops.smithy"), + renameErrors = false, + ), + ClientTest( + "casing#ACRONYMInside_Service", + "naming_test_casing", + dependsOn = listOf("naming-obstacle-course-casing.smithy"), + ), + ClientTest( + "naming_obs_structs#NamingObstacleCourseStructs", + "naming_test_structs", + dependsOn = listOf("naming-obstacle-course-structs.smithy"), + renameErrors = false, + ), + ClientTest("aws.protocoltests.json#TestService", "endpoint-rules"), + ClientTest( + "com.aws.example#PokemonService", + "pokemon-service-client", + dependsOn = listOf("pokemon.smithy", "pokemon-common.smithy"), + ), + ClientTest( + "com.aws.example#PokemonService", + "pokemon-service-awsjson-client", + dependsOn = listOf("pokemon-awsjson.smithy", "pokemon-common.smithy"), + ), +).map(ClientTest::toCodegenTest) + project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests) project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) +tasks["generateSmithyBuild"].inputs.property("smithy.runtime.mode", getSmithyRuntimeMode()) + tasks["smithyBuildJar"].dependsOn("generateSmithyBuild") tasks["assemble"].finalizedBy("generateCargoWorkspace") diff --git a/codegen-client/build.gradle.kts b/codegen-client/build.gradle.kts index 62d543beb4..919c1fe9bb 100644 --- a/codegen-client/build.gradle.kts +++ b/codegen-client/build.gradle.kts @@ -77,12 +77,11 @@ if (isTestingEnabled.toBoolean()) { tasks.test { useJUnitPlatform() testLogging { - events("passed", "skipped", "failed") + events("failed") exceptionFormat = TestExceptionFormat.FULL showCauses = true showExceptions = true showStackTraces = true - showStandardStreams = true } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt index e0ec0afb6e..7491b0e8e3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol /** * [ClientCodegenContext] contains code-generation context that is _specific_ to the [RustClientCodegenPlugin] plugin @@ -30,6 +31,10 @@ data class ClientCodegenContext( // Expose the `rootDecorator`, enabling customizations to compose by referencing information from the root codegen // decorator val rootDecorator: ClientCodegenDecorator, + val protocolImpl: Protocol? = null, ) : CodegenContext( model, symbolProvider, moduleDocProvider, serviceShape, protocol, settings, CodegenTarget.CLIENT, -) +) { + val smithyRuntimeMode: SmithyRuntimeMode get() = settings.codegenConfig.enableNewSmithyRuntime + val enableUserConfigurableRuntimePlugins: Boolean get() = settings.codegenConfig.enableUserConfigurableRuntimePlugins +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index d7ce6440e4..b8cf2605de 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -20,11 +20,11 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientEnumGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.error.OperationErrorGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.DefaultProtocolTestGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientProtocolLoader import software.amazon.smithy.rust.codegen.client.smithy.transformers.AddErrorMessage import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEventStreamOperations @@ -45,7 +45,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGenerat import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -71,8 +71,8 @@ class ClientCodegenVisitor( private val fileManifest = context.fileManifest private val model: Model private var codegenContext: ClientCodegenContext - private val protocolGeneratorFactory: ProtocolGeneratorFactory - private val protocolGenerator: ClientProtocolGenerator + private val protocolGeneratorFactory: ProtocolGeneratorFactory + private val operationGenerator: OperationGenerator init { val rustSymbolProviderConfig = RustSymbolProviderConfig( @@ -88,7 +88,7 @@ class ClientCodegenVisitor( codegenDecorator.protocols(untransformedService.id, ClientProtocolLoader.DefaultProtocols), ).protocolFor(context.model, untransformedService) protocolGeneratorFactory = generator - model = codegenDecorator.transformModel(untransformedService, baseModel) + model = codegenDecorator.transformModel(untransformedService, baseModel, settings) // the model transformer _might_ change the service shape val service = settings.getService(model) symbolProvider = RustClientCodegenPlugin.baseSymbolProvider(settings, model, service, rustSymbolProviderConfig, codegenDecorator) @@ -108,6 +108,7 @@ class ClientCodegenVisitor( codegenContext, ClientModuleDocProvider(codegenContext, service.serviceNameOrDefault("the service")), ), + protocolImpl = protocolGeneratorFactory.protocol(codegenContext), ) rustCrate = RustCrate( @@ -116,7 +117,7 @@ class ClientCodegenVisitor( codegenContext.settings.codegenConfig, codegenContext.expectModuleDocProvider(), ) - protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) + operationGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) } /** @@ -169,8 +170,9 @@ class ClientCodegenVisitor( ), ) try { - "cargo fmt".runCommand(fileManifest.baseDir, timeout = settings.codegenConfig.formatTimeoutSeconds.toLong()) - } catch (err: CommandFailed) { + // use an increased max_width to make rustfmt fail less frequently + "cargo fmt -- --config max_width=150".runCommand(fileManifest.baseDir, timeout = settings.codegenConfig.formatTimeoutSeconds.toLong()) + } catch (err: CommandError) { logger.warning("Failed to run cargo fmt: [${service.id}]\n${err.output}") } @@ -303,30 +305,32 @@ class ClientCodegenVisitor( rustCrate.useShapeWriter(operationShape) operationWriter@{ rustCrate.useShapeWriter(operationShape.inputShape(codegenContext.model)) inputWriter@{ // Render the operation shape & serializers input `input.rs` - protocolGenerator.renderOperation( + operationGenerator.renderOperation( this@operationWriter, this@inputWriter, operationShape, - codegenDecorator.operationCustomizations(codegenContext, operationShape, listOf()), + codegenDecorator, ) // render protocol tests into `operation.rs` (note operationWriter vs. inputWriter) - ProtocolTestGenerator( + codegenDecorator.protocolTestGenerator( codegenContext, - protocolGeneratorFactory.support(), - operationShape, - this@operationWriter, - ).render() + DefaultProtocolTestGenerator( + codegenContext, + protocolGeneratorFactory.support(), + operationShape, + ), + ).render(this@operationWriter) } - } - rustCrate.withModule(symbolProvider.moduleForOperationError(operationShape)) { - OperationErrorGenerator( - model, - symbolProvider, - operationShape, - codegenDecorator.errorCustomizations(codegenContext, emptyList()), - ).render(this) + rustCrate.withModule(symbolProvider.moduleForOperationError(operationShape)) { + OperationErrorGenerator( + model, + symbolProvider, + operationShape, + codegenDecorator.errorCustomizations(codegenContext, emptyList()), + ).render(this) + } } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt index aa1f547dc4..afe7b70574 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt @@ -26,6 +26,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider import software.amazon.smithy.rust.codegen.core.smithy.ModuleProvider import software.amazon.smithy.rust.codegen.core.smithy.ModuleProviderContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait @@ -53,9 +54,37 @@ object ClientRustModule { val customize = RustModule.public("customize", parent = self) } - val Config = RustModule.public("config") - val Error = RustModule.public("error") + /** crate::config */ + val config = Config.self + object Config { + /** crate::client */ + val self = RustModule.public("config") + + /** crate::config::endpoint */ + val endpoint = RustModule.public("endpoint", parent = self) + + /** crate::config::retry */ + val retry = RustModule.public("retry", parent = self) + + /** crate::config::timeout */ + val timeout = RustModule.public("timeout", parent = self) + + /** crate::config::interceptors */ + val interceptors = RustModule.public("interceptors", parent = self) + } + + // TODO(enableNewSmithyRuntimeCleanup): Delete this root endpoint module + @Deprecated(message = "use the endpoint() method to get the endpoint module for now") val Endpoint = RustModule.public("endpoint") + + // TODO(enableNewSmithyRuntimeCleanup): Just use Config.endpoint directly and delete this function + fun endpoint(codegenContext: ClientCodegenContext): RustModule.LeafModule = if (codegenContext.smithyRuntimeMode.generateMiddleware) { + Endpoint + } else { + Config.endpoint + } + + val Error = RustModule.public("error") val Operation = RustModule.public("operation") val Meta = RustModule.public("meta") val Input = RustModule.public("input") @@ -82,7 +111,11 @@ class ClientModuleDocProvider( return when (module) { ClientRustModule.client -> clientModuleDoc() ClientRustModule.Client.customize -> customizeModuleDoc() - ClientRustModule.Config -> strDoc("Configuration for $serviceName.") + ClientRustModule.config -> strDoc("Configuration for $serviceName.") + ClientRustModule.Config.endpoint -> strDoc("Types needed to configure endpoint resolution.") + ClientRustModule.Config.retry -> strDoc("Retry configuration.") + ClientRustModule.Config.timeout -> strDoc("Timeout configuration.") + ClientRustModule.Config.interceptors -> strDoc("Types needed to implement [`Interceptor`](crate::config::Interceptor).") ClientRustModule.Error -> strDoc("Common errors and error handling utilities.") ClientRustModule.Endpoint -> strDoc("Endpoint resolution functionality.") ClientRustModule.Operation -> strDoc("All operations that this crate can perform.") @@ -122,7 +155,7 @@ class ClientModuleDocProvider( operation call. For example, this can be used to add an additional HTTP header: ```ignore - ## async fn wrapper() -> Result<(), $moduleUseName::Error> { + ## async fn wrapper() -> #{Result}<(), $moduleUseName::Error> { ## let client: $moduleUseName::Client = unimplemented!(); use #{http}::header::{HeaderName, HeaderValue}; @@ -142,6 +175,7 @@ class ClientModuleDocProvider( ## } ``` """.trimIndent(), + *RuntimeType.preludeScope, "http" to CargoDependency.Http.toDevDependency().toType(), ) } @@ -194,6 +228,10 @@ object ClientModuleProvider : ModuleProvider { operationModuleName, parent = ClientRustModule.Operation, documentationOverride = "Types for the `$contextName` operation.", + // TODO(https://github.com/tokio-rs/tokio/issues/5683): Uncomment the NoImplicitPrelude attribute once this Tokio issue is resolved + // // Disable the Rust prelude since every prelude type should be referenced with its + // // fully qualified name to avoid name collisions with the generated operation shapes. + // additionalAttributes = listOf(Attribute.NoImplicitPrelude) ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt index e6680d3e0e..c7268a67a3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt @@ -72,6 +72,23 @@ data class ClientRustSettings( } } +// TODO(enableNewSmithyRuntimeCleanup): Remove this mode after switching to the orchestrator +enum class SmithyRuntimeMode { + Middleware, Orchestrator, + ; + + val generateMiddleware: Boolean get() = this == Middleware + val generateOrchestrator: Boolean get() = this == Orchestrator + + companion object { + fun fromString(value: String): SmithyRuntimeMode = when (value) { + "middleware" -> Middleware + "orchestrator" -> Orchestrator + else -> throw IllegalArgumentException("unknown runtime mode: $value") + } + } +} + /** * [renameExceptions]: Rename `Exception` to `Error` in the generated SDK * [includeFluentClient]: Generate a `client` module in the generated SDK (currently the AWS SDK sets this to `false` @@ -86,8 +103,11 @@ data class ClientCodegenConfig( val addMessageToErrors: Boolean = defaultAddMessageToErrors, // TODO(EventStream): [CLEANUP] Remove this property when turning on Event Stream for all services val eventStreamAllowList: Set = defaultEventStreamAllowList, - // TODO(SmithyRuntime): Remove this once we commit to switch to aws-smithy-runtime and aws-smithy-runtime-api - val enableNewSmithyRuntime: Boolean = defaultEnableNewSmithyRuntime, + // TODO(enableNewSmithyRuntimeCleanup): Remove this once we commit to switch to aws-smithy-runtime and aws-smithy-runtime-api + val enableNewSmithyRuntime: SmithyRuntimeMode = defaultEnableNewSmithyRuntime, + /** If true, adds `endpoint_url`/`set_endpoint_url` methods to the service config */ + val includeEndpointUrlConfig: Boolean = defaultIncludeEndpointUrlConfig, + val enableUserConfigurableRuntimePlugins: Boolean = defaultEnableUserConfigurableRuntimePlugins, ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, ) { @@ -96,20 +116,26 @@ data class ClientCodegenConfig( private const val defaultIncludeFluentClient = true private const val defaultAddMessageToErrors = true private val defaultEventStreamAllowList: Set = emptySet() - private const val defaultEnableNewSmithyRuntime = false + private val defaultEnableNewSmithyRuntime = SmithyRuntimeMode.Orchestrator + private const val defaultIncludeEndpointUrlConfig = true + private const val defaultEnableUserConfigurableRuntimePlugins = true fun fromCodegenConfigAndNode(coreCodegenConfig: CoreCodegenConfig, node: Optional) = if (node.isPresent) { ClientCodegenConfig( formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds, debugMode = coreCodegenConfig.debugMode, - eventStreamAllowList = node.get().getArrayMember("eventStreamAllowList") - .map { array -> array.toList().mapNotNull { node -> node.asStringNode().orNull()?.value } } - .orNull()?.toSet() ?: defaultEventStreamAllowList, + eventStreamAllowList = node.get().getArrayMember("eventStreamAllowList").map { array -> + array.toList().mapNotNull { node -> + node.asStringNode().orNull()?.value + } + }.orNull()?.toSet() ?: defaultEventStreamAllowList, renameExceptions = node.get().getBooleanMemberOrDefault("renameErrors", defaultRenameExceptions), includeFluentClient = node.get().getBooleanMemberOrDefault("includeFluentClient", defaultIncludeFluentClient), addMessageToErrors = node.get().getBooleanMemberOrDefault("addMessageToErrors", defaultAddMessageToErrors), - enableNewSmithyRuntime = node.get().getBooleanMemberOrDefault("enableNewSmithyRuntime", defaultEnableNewSmithyRuntime), + enableNewSmithyRuntime = SmithyRuntimeMode.fromString(node.get().getStringMemberOrDefault("enableNewSmithyRuntime", "middleware")), + includeEndpointUrlConfig = node.get().getBooleanMemberOrDefault("includeEndpointUrlConfig", defaultIncludeEndpointUrlConfig), + enableUserConfigurableRuntimePlugins = node.get().getBooleanMemberOrDefault("enableUserConfigurableRuntimePlugins", defaultEnableUserConfigurableRuntimePlugins), ) } else { ClientCodegenConfig( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index f8852a700d..7b05e664e8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -11,10 +11,13 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.customizations.ApiKeyAuthDecorator import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations +import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpAuthDecorator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpConnectorConfigDecorator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.NoAuthDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointParamsDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointsDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientDecorator import software.amazon.smithy.rust.codegen.client.testutil.ClientDecoratableBuildPlugin @@ -58,8 +61,11 @@ class RustClientCodegenPlugin : ClientDecoratableBuildPlugin() { RequiredCustomizations(), FluentClientDecorator(), EndpointsDecorator(), - NoOpEventStreamSigningDecorator(), + EndpointParamsDecorator(), + NoAuthDecorator(), ApiKeyAuthDecorator(), + HttpAuthDecorator(), + HttpConnectorConfigDecorator(), *decorator, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index 0d9f5bde46..fb02e28271 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -14,6 +14,8 @@ import software.amazon.smithy.model.traits.Trait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -23,12 +25,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.letIf +// TODO(enableNewSmithyRuntimeCleanup): Delete this decorator when switching to the orchestrator + /** * Inserts a ApiKeyAuth configuration into the operation */ @@ -37,14 +39,15 @@ class ApiKeyAuthDecorator : ClientCodegenDecorator { override val order: Byte = 10 private fun applies(codegenContext: ClientCodegenContext) = - isSupportedApiKeyAuth(codegenContext) + codegenContext.smithyRuntimeMode.generateMiddleware && + isSupportedApiKeyAuth(codegenContext) override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { return baseCustomizations.letIf(applies(codegenContext)) { customizations -> - customizations + ApiKeyConfigCustomization(codegenContext.runtimeConfig) + customizations + ApiKeyConfigCustomization(codegenContext) } } @@ -63,7 +66,7 @@ class ApiKeyAuthDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { if (applies(codegenContext)) { - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rust("pub use #T;", apiKey(codegenContext.runtimeConfig)) } } @@ -154,15 +157,18 @@ private class ApiKeyOperationCustomization(private val runtimeConfig: RuntimeCon } } -private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { +private class ApiKeyConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { + val runtimeMode = codegenContext.smithyRuntimeMode + val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = arrayOf( + *preludeScope, "ApiKey" to apiKey(runtimeConfig), ) override fun section(section: ServiceConfig): Writable = when (section) { is ServiceConfig.BuilderStruct -> writable { - rustTemplate("api_key: Option<#{ApiKey}>,", *codegenScope) + rustTemplate("api_key: #{Option}<#{ApiKey}>,", *codegenScope) } is ServiceConfig.BuilderImpl -> writable { rustTemplate( @@ -174,7 +180,7 @@ private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCu } /// Sets the API key that will be used by the client. - pub fn set_api_key(&mut self, api_key: Option<#{ApiKey}>) -> &mut Self { + pub fn set_api_key(&mut self, api_key: #{Option}<#{ApiKey}>) -> &mut Self { self.api_key = api_key; self } @@ -183,21 +189,39 @@ private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCu ) } is ServiceConfig.BuilderBuild -> writable { - rust("api_key: self.api_key,") + if (runtimeMode.generateOrchestrator) { + rust("layer.store_or_unset(self.api_key);") + } else { + rust("api_key: self.api_key,") + } } is ServiceConfig.ConfigStruct -> writable { - rustTemplate("api_key: Option<#{ApiKey}>,", *codegenScope) + if (runtimeMode.generateMiddleware) { + rustTemplate("api_key: #{Option}<#{ApiKey}>,", *codegenScope) + } } is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Returns API key used by the client, if it was provided. - pub fn api_key(&self) -> Option<&#{ApiKey}> { - self.api_key.as_ref() - } - """, - *codegenScope, - ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Returns API key used by the client, if it was provided. + pub fn api_key(&self) -> #{Option}<&#{ApiKey}> { + self.config.load::<#{ApiKey}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns API key used by the client, if it was provided. + pub fn api_key(&self) -> #{Option}<&#{ApiKey}> { + self.api_key.as_ref() + } + """, + *codegenScope, + ) + } } else -> emptySection } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt new file mode 100644 index 0000000000..cd797f938f --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.smithyRuntime + +class ConnectionPoisoningRuntimePluginCustomization( + codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { + // This interceptor assumes that a compatible Connector is set. Otherwise, connection poisoning + // won't work and an error message will be logged. + section.registerInterceptor(runtimeConfig, this) { + rust( + "#T::new()", + smithyRuntime(runtimeConfig).resolve("client::connectors::connection_poisoning::ConnectionPoisoningInterceptor"), + ) + } + } + + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt index 110ca580c6..60ac6c631a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt @@ -7,33 +7,43 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.generators.EndpointTraitBindings +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.util.orNull -class EndpointPrefixGenerator(private val codegenContext: CodegenContext, private val shape: OperationShape) : +// TODO(enableNewSmithyRuntimeCleanup): Delete this file + +class EndpointPrefixGenerator(private val codegenContext: ClientCodegenContext, private val shape: OperationShape) : OperationCustomization() { - override fun section(section: OperationSection): Writable = when (section) { - is OperationSection.MutateRequest -> writable { + companion object { + fun endpointTraitBindings(codegenContext: ClientCodegenContext, shape: OperationShape): EndpointTraitBindings? = shape.getTrait(EndpointTrait::class.java).map { epTrait -> - val endpointTraitBindings = EndpointTraitBindings( + EndpointTraitBindings( codegenContext.model, codegenContext.symbolProvider, codegenContext.runtimeConfig, shape, epTrait, ) + }.orNull() + } + + override fun section(section: OperationSection): Writable = when (section) { + is OperationSection.MutateRequest -> writable { + endpointTraitBindings(codegenContext, shape)?.also { endpointTraitBindings -> withBlock("let endpoint_prefix = ", "?;") { - endpointTraitBindings.render(this, "self") + endpointTraitBindings.render(this, "self", codegenContext.smithyRuntimeMode) } rust("request.properties_mut().insert(endpoint_prefix);") } } + else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt new file mode 100644 index 0000000000..9477f1082a --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -0,0 +1,313 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.model.knowledge.ServiceIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.HttpApiKeyAuthTrait +import software.amazon.smithy.model.traits.HttpBasicAuthTrait +import software.amazon.smithy.model.traits.HttpBearerAuthTrait +import software.amazon.smithy.model.traits.HttpDigestAuthTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption.StaticAuthSchemeOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.letIf + +private fun codegenScope(runtimeConfig: RuntimeConfig): Array> { + val smithyRuntime = + CargoDependency.smithyRuntime(runtimeConfig).withFeature("http-auth").toType() + val smithyRuntimeApi = CargoDependency.smithyRuntimeApi(runtimeConfig).withFeature("http-auth").toType() + val authHttp = smithyRuntime.resolve("client::auth::http") + val authHttpApi = smithyRuntimeApi.resolve("client::auth::http") + return arrayOf( + "AuthSchemeId" to smithyRuntimeApi.resolve("client::auth::AuthSchemeId"), + "ApiKeyAuthScheme" to authHttp.resolve("ApiKeyAuthScheme"), + "ApiKeyLocation" to authHttp.resolve("ApiKeyLocation"), + "BasicAuthScheme" to authHttp.resolve("BasicAuthScheme"), + "BearerAuthScheme" to authHttp.resolve("BearerAuthScheme"), + "DigestAuthScheme" to authHttp.resolve("DigestAuthScheme"), + "HTTP_API_KEY_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_API_KEY_AUTH_SCHEME_ID"), + "HTTP_BASIC_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_BASIC_AUTH_SCHEME_ID"), + "HTTP_BEARER_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_BEARER_AUTH_SCHEME_ID"), + "HTTP_DIGEST_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_DIGEST_AUTH_SCHEME_ID"), + "IdentityResolver" to smithyRuntimeApi.resolve("client::identity::IdentityResolver"), + "Login" to smithyRuntimeApi.resolve("client::identity::http::Login"), + "PropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::PropertyBag"), + "SharedAuthScheme" to smithyRuntimeApi.resolve("client::auth::SharedAuthScheme"), + "SharedIdentityResolver" to smithyRuntimeApi.resolve("client::identity::SharedIdentityResolver"), + "Token" to smithyRuntimeApi.resolve("client::identity::http::Token"), + ) +} + +private data class HttpAuthSchemes( + val apiKey: Boolean, + val basic: Boolean, + val bearer: Boolean, + val digest: Boolean, +) { + companion object { + fun from(codegenContext: ClientCodegenContext): HttpAuthSchemes { + val authSchemes = ServiceIndex.of(codegenContext.model).getAuthSchemes(codegenContext.serviceShape).keys + val generateOrchestrator = codegenContext.smithyRuntimeMode.generateOrchestrator + return HttpAuthSchemes( + apiKey = generateOrchestrator && authSchemes.contains(HttpApiKeyAuthTrait.ID), + basic = generateOrchestrator && authSchemes.contains(HttpBasicAuthTrait.ID), + bearer = generateOrchestrator && authSchemes.contains(HttpBearerAuthTrait.ID), + digest = generateOrchestrator && authSchemes.contains(HttpDigestAuthTrait.ID), + ) + } + } + + fun anyEnabled(): Boolean = isTokenBased() || isLoginBased() + fun isTokenBased(): Boolean = apiKey || bearer + fun isLoginBased(): Boolean = basic || digest +} + +class HttpAuthDecorator : ClientCodegenDecorator { + override val name: String get() = "HttpAuthDecorator" + override val order: Byte = 0 + + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthSchemeOptions: List, + ): List { + val serviceIndex = ServiceIndex.of(codegenContext.model) + val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape) + val codegenScope = codegenScope(codegenContext.runtimeConfig) + val options = ArrayList() + for (authScheme in authSchemes.keys) { + fun addOption(schemeShapeId: ShapeId, name: String) { + options.add( + StaticAuthSchemeOption( + schemeShapeId, + writable { + rustTemplate("$name,", *codegenScope) + }, + ), + ) + } + when (authScheme) { + HttpApiKeyAuthTrait.ID -> addOption(authScheme, "#{HTTP_API_KEY_AUTH_SCHEME_ID}") + HttpBasicAuthTrait.ID -> addOption(authScheme, "#{HTTP_BASIC_AUTH_SCHEME_ID}") + HttpBearerAuthTrait.ID -> addOption(authScheme, "#{HTTP_BEARER_AUTH_SCHEME_ID}") + HttpDigestAuthTrait.ID -> addOption(authScheme, "#{HTTP_DIGEST_AUTH_SCHEME_ID}") + else -> {} + } + } + return baseAuthSchemeOptions + options + } + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + HttpAuthSchemes.from(codegenContext).let { authSchemes -> + baseCustomizations.letIf(authSchemes.anyEnabled()) { + it + HttpAuthConfigCustomization(codegenContext, authSchemes) + } + } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + HttpAuthSchemes.from(codegenContext).let { authSchemes -> + baseCustomizations.letIf(authSchemes.anyEnabled()) { + it + HttpAuthServiceRuntimePluginCustomization(codegenContext, authSchemes) + } + } + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + val authSchemes = HttpAuthSchemes.from(codegenContext) + if (authSchemes.anyEnabled()) { + rustCrate.withModule(ClientRustModule.config) { + val codegenScope = codegenScope(codegenContext.runtimeConfig) + if (authSchemes.isTokenBased()) { + rustTemplate("pub use #{Token};", *codegenScope) + } + if (authSchemes.isLoginBased()) { + rustTemplate("pub use #{Login};", *codegenScope) + } + } + } + } +} + +private class HttpAuthServiceRuntimePluginCustomization( + private val codegenContext: ClientCodegenContext, + private val authSchemes: HttpAuthSchemes, +) : ServiceRuntimePluginCustomization() { + private val serviceShape = codegenContext.serviceShape + private val codegenScope = codegenScope(codegenContext.runtimeConfig) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { + fun registerAuthScheme(scheme: Writable) { + section.registerAuthScheme(this) { + rustTemplate("#{SharedAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme) + } + } + fun registerNamedAuthScheme(name: String) { + registerAuthScheme { + rustTemplate("#{$name}::new()", *codegenScope) + } + } + + if (authSchemes.apiKey) { + val trait = serviceShape.getTrait()!! + val location = when (trait.`in`!!) { + HttpApiKeyAuthTrait.Location.HEADER -> { + check(trait.scheme.isPresent) { + "A scheme is required for `@httpApiKey` when `in` is set to `header`" + } + "Header" + } + + HttpApiKeyAuthTrait.Location.QUERY -> "Query" + } + + registerAuthScheme { + rustTemplate( + """ + #{ApiKeyAuthScheme}::new( + ${trait.scheme.orElse("").dq()}, + #{ApiKeyLocation}::$location, + ${trait.name.dq()}, + ) + """, + *codegenScope, + ) + } + } + if (authSchemes.basic) { + registerNamedAuthScheme("BasicAuthScheme") + } + if (authSchemes.bearer) { + registerNamedAuthScheme("BearerAuthScheme") + } + if (authSchemes.digest) { + registerNamedAuthScheme("DigestAuthScheme") + } + } + + else -> emptySection + } + } +} + +private class HttpAuthConfigCustomization( + codegenContext: ClientCodegenContext, + private val authSchemes: HttpAuthSchemes, +) : ConfigCustomization() { + private val codegenScope = codegenScope(codegenContext.runtimeConfig) + + override fun section(section: ServiceConfig): Writable = writable { + when (section) { + is ServiceConfig.BuilderImpl -> { + if (authSchemes.apiKey) { + rustTemplate( + """ + /// Sets the API key that will be used for authentication. + pub fn api_key(self, api_key: #{Token}) -> Self { + self.api_key_resolver(api_key) + } + + /// Sets an API key resolver will be used for authentication. + pub fn api_key_resolver(mut self, api_key_resolver: impl #{IdentityResolver} + 'static) -> Self { + self.runtime_components.push_identity_resolver( + #{HTTP_API_KEY_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(api_key_resolver) + ); + self + } + """, + *codegenScope, + ) + } + if (authSchemes.bearer) { + rustTemplate( + """ + /// Sets the bearer token that will be used for HTTP bearer auth. + pub fn bearer_token(self, bearer_token: #{Token}) -> Self { + self.bearer_token_resolver(bearer_token) + } + + /// Sets a bearer token provider that will be used for HTTP bearer auth. + pub fn bearer_token_resolver(mut self, bearer_token_resolver: impl #{IdentityResolver} + 'static) -> Self { + self.runtime_components.push_identity_resolver( + #{HTTP_BEARER_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(bearer_token_resolver) + ); + self + } + """, + *codegenScope, + ) + } + if (authSchemes.basic) { + rustTemplate( + """ + /// Sets the login that will be used for HTTP basic auth. + pub fn basic_auth_login(self, basic_auth_login: #{Login}) -> Self { + self.basic_auth_login_resolver(basic_auth_login) + } + + /// Sets a login resolver that will be used for HTTP basic auth. + pub fn basic_auth_login_resolver(mut self, basic_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { + self.runtime_components.push_identity_resolver( + #{HTTP_BASIC_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(basic_auth_resolver) + ); + self + } + """, + *codegenScope, + ) + } + if (authSchemes.digest) { + rustTemplate( + """ + /// Sets the login that will be used for HTTP digest auth. + pub fn digest_auth_login(self, digest_auth_login: #{Login}) -> Self { + self.digest_auth_login_resolver(digest_auth_login) + } + + /// Sets a login resolver that will be used for HTTP digest auth. + pub fn digest_auth_login_resolver(mut self, digest_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { + self.runtime_components.push_identity_resolver( + #{HTTP_DIGEST_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(digest_auth_resolver) + ); + self + } + """, + *codegenScope, + ) + } + } + + else -> {} + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt index bf275034c6..e654b48421 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt @@ -8,13 +8,18 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.toType import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -32,6 +37,22 @@ class HttpChecksumRequiredGenerator( throw CodegenException("HttpChecksum required cannot be applied to a streaming shape") } return when (section) { + is OperationSection.AdditionalRuntimePlugins -> writable { + section.addOperationRuntimePlugin(this) { + rustTemplate( + "#{HttpChecksumRequiredRuntimePlugin}::new()", + "HttpChecksumRequiredRuntimePlugin" to + InlineDependency.forRustFile( + RustModule.pubCrate("client_http_checksum_required", parent = ClientRustModule.root), + "/inlineable/src/client_http_checksum_required.rs", + CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig), + CargoDependency.smithyTypes(codegenContext.runtimeConfig), + CargoDependency.Http, + CargoDependency.Md5, + ).toType().resolve("HttpChecksumRequiredRuntimePlugin"), + ) + } + } is OperationSection.MutateRequest -> writable { rustTemplate( """ diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt new file mode 100644 index 0000000000..49dbce324b --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -0,0 +1,278 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.util.letIf + +class HttpConnectorConfigDecorator : ClientCodegenDecorator { + override val name: String = "HttpConnectorConfigDecorator" + override val order: Byte = 0 + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + HttpConnectorConfigCustomization(codegenContext) + } +} + +private class HttpConnectorConfigCustomization( + codegenContext: ClientCodegenContext, +) : ConfigCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode + private val moduleUseName = codegenContext.moduleUseName() + private val codegenScope = arrayOf( + *preludeScope, + "Connection" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::Connection"), + "ConnectorSettings" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::ConnectorSettings"), + "DynConnectorAdapter" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::connectors::adapter::DynConnectorAdapter"), + "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), + "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), + "SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"), + "SharedHttpConnector" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::connectors::SharedHttpConnector"), + "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), + ) + + private fun defaultConnectorFn(): RuntimeType = RuntimeType.forInlineFun("default_connector", ClientRustModule.config) { + rustTemplate( + """ + ##[cfg(feature = "rustls")] + fn default_connector( + connector_settings: &#{ConnectorSettings}, + sleep_impl: #{Option}<#{SharedAsyncSleep}>, + ) -> #{Option}<#{DynConnector}> { + #{default_connector}(connector_settings, sleep_impl) + } + + ##[cfg(not(feature = "rustls"))] + fn default_connector( + _connector_settings: &#{ConnectorSettings}, + _sleep_impl: #{Option}<#{SharedAsyncSleep}>, + ) -> #{Option}<#{DynConnector}> { + #{None} + } + """, + *codegenScope, + "default_connector" to RuntimeType.smithyClient(runtimeConfig).resolve("conns::default_connector"), + "DynConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("erase::DynConnector"), + ) + } + + private fun setConnectorFn(): RuntimeType = RuntimeType.forInlineFun("set_connector", ClientRustModule.config) { + rustTemplate( + """ + fn set_connector(resolver: &mut #{Resolver}<'_>) { + // Initial configuration needs to set a default if no connector is given, so it + // should always get into the condition below. + // + // Override configuration should set the connector if the override config + // contains a connector, sleep impl, or a timeout config since these are all + // incorporated into the final connector. + let must_set_connector = resolver.is_initial() + || resolver.is_latest_set::<#{HttpConnector}>() + || resolver.latest_sleep_impl().is_some() + || resolver.is_latest_set::<#{TimeoutConfig}>(); + if must_set_connector { + let sleep_impl = resolver.sleep_impl(); + let timeout_config = resolver.resolve_config::<#{TimeoutConfig}>() + .cloned() + .unwrap_or_else(#{TimeoutConfig}::disabled); + let connector_settings = #{ConnectorSettings}::from_timeout_config(&timeout_config); + let http_connector = resolver.resolve_config::<#{HttpConnector}>(); + + // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation + let connector = + http_connector + .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) + .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) + .map(|c| #{SharedHttpConnector}::new(#{DynConnectorAdapter}::new(c))); + + resolver.runtime_components_mut().set_http_connector(connector); + } + } + """, + *codegenScope, + "default_connector" to defaultConnectorFn(), + ) + } + + override fun section(section: ServiceConfig): Writable { + return when (section) { + is ServiceConfig.ConfigStruct -> writable { + if (runtimeMode.generateMiddleware) { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } + } + + is ServiceConfig.ConfigImpl -> writable { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Return the [`SharedHttpConnector`](#{SharedHttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<#{SharedHttpConnector}> { + self.runtime_components.http_connector() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.http_connector.as_ref() + } + """, + *codegenScope, + ) + } + } + + is ServiceConfig.BuilderStruct -> writable { + if (runtimeMode.generateMiddleware) { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } + } + + ServiceConfig.BuilderImpl -> writable { + rustTemplate( + """ + /// Sets the HTTP connector to use when making requests. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use std::time::Duration; + /// use aws_smithy_client::{Client, hyper_ext}; + /// use aws_smithy_client::erase::DynConnector; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// use $moduleUseName::config::Config; + /// + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// ## } + /// ## } + /// ``` + pub fn http_connector(mut self, http_connector: impl Into<#{HttpConnector}>) -> Self { + self.set_http_connector(#{Some}(http_connector)); + self + } + + /// Sets the HTTP connector to use when making requests. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use std::time::Duration; + /// use aws_smithy_client::hyper_ext; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// use $moduleUseName::config::{Builder, Config}; + /// + /// fn override_http_connector(builder: &mut Builder) { + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// builder.set_http_connector(Some(smithy_connector)); + /// } + /// + /// let mut builder = $moduleUseName::Config::builder(); + /// override_http_connector(&mut builder); + /// let config = builder.build(); + /// ## } + /// ## } + /// ``` + """, + *codegenScope, + ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { + http_connector.map(|c| self.config.store_put(c.into())); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { + self.http_connector = http_connector.map(|inner| inner.into()); + self + } + """, + *codegenScope, + ) + } + } + + is ServiceConfig.BuilderBuild -> writable { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + "#{set_connector}(&mut resolver);", + "set_connector" to setConnectorFn(), + ) + } else { + rust("http_connector: self.http_connector,") + } + } + + is ServiceConfig.OperationConfigOverride -> writable { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + "#{set_connector}(&mut resolver);", + "set_connector" to setConnectorFn(), + ) + } + } + + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt index da4ba53222..43dab5ba8c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt @@ -7,16 +7,18 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.aws.traits.protocols.AwsProtocolTrait import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.isEventStream +// TODO(enableNewSmithyRuntimeCleanup): Delete this file + class HttpVersionListCustomization( private val codegenContext: CodegenContext, private val operationShape: OperationShape, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt index 8dd67bb9fe..3ee06bcea5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt @@ -7,33 +7,68 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.toType import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.util.findMemberWithTrait import software.amazon.smithy.rust.codegen.core.util.inputShape -class IdempotencyTokenGenerator(codegenContext: CodegenContext, operationShape: OperationShape) : - OperationCustomization() { +class IdempotencyTokenGenerator( + codegenContext: CodegenContext, + operationShape: OperationShape, +) : OperationCustomization() { private val model = codegenContext.model + private val runtimeConfig = codegenContext.runtimeConfig private val symbolProvider = codegenContext.symbolProvider - private val idempotencyTokenMember = operationShape.inputShape(model).findMemberWithTrait(model) + private val inputShape = operationShape.inputShape(model) + private val idempotencyTokenMember = inputShape.findMemberWithTrait(model) + override fun section(section: OperationSection): Writable { if (idempotencyTokenMember == null) { return emptySection } val memberName = symbolProvider.toMemberName(idempotencyTokenMember) return when (section) { + is OperationSection.AdditionalRuntimePlugins -> writable { + section.addOperationRuntimePlugin(this) { + rustTemplate( + """ + #{IdempotencyTokenRuntimePlugin}::new(|token_provider, input| { + let input: &mut #{Input} = input.downcast_mut().expect("correct type"); + if input.$memberName.is_none() { + input.$memberName = #{Some}(token_provider.make_idempotency_token()); + } + }) + """, + *preludeScope, + "Input" to symbolProvider.toSymbol(inputShape), + "IdempotencyTokenRuntimePlugin" to + InlineDependency.forRustFile( + RustModule.pubCrate("client_idempotency_token", parent = ClientRustModule.root), + "/inlineable/src/client_idempotency_token.rs", + CargoDependency.smithyRuntimeApi(runtimeConfig), + CargoDependency.smithyTypes(runtimeConfig), + ).toType().resolve("IdempotencyTokenRuntimePlugin"), + ) + } + } is OperationSection.MutateInput -> writable { - rust( + rustTemplate( """ if ${section.input}.$memberName.is_none() { - ${section.input}.$memberName = Some(${section.config}.make_token.make_idempotency_token()); + ${section.input}.$memberName = #{Some}(${section.config}.idempotency_token_provider.make_idempotency_token()); } """, + *preludeScope, ) } else -> emptySection diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt new file mode 100644 index 0000000000..9bb9e16ef3 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -0,0 +1,165 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType + +class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val moduleUseName = codegenContext.moduleUseName() + private val runtimeConfig = codegenContext.runtimeConfig + + // TODO(enableNewSmithyRuntimeCleanup): Remove the writable below + private val maybeHideOrchestratorCode = writable { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rust("##[doc(hidden)]") + } + } + private val codegenScope = arrayOf( + "Interceptor" to RuntimeType.interceptor(runtimeConfig), + "SharedInterceptor" to RuntimeType.sharedInterceptor(runtimeConfig), + "maybe_hide_orchestrator_code" to maybeHideOrchestratorCode, + ) + + override fun section(section: ServiceConfig) = + writable { + when (section) { + ServiceConfig.ConfigImpl -> rustTemplate( + """ + #{maybe_hide_orchestrator_code} + /// Returns interceptors currently registered by the user. + pub fn interceptors(&self) -> impl Iterator + '_ { + self.runtime_components.interceptors() + } + """, + *codegenScope, + ) + + ServiceConfig.BuilderImpl -> + rustTemplate( + """ + #{maybe_hide_orchestrator_code} + /// Add an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline. + /// + /// Interceptors targeted at a certain stage are executed according to the pre-defined priority. + /// The SDK provides a default set of interceptors. An interceptor configured by this method + /// will run after those default interceptors. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; + /// use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; + /// use aws_smithy_types::config_bag::ConfigBag; + /// use $moduleUseName::config::Config; + /// + /// fn base_url() -> String { + /// // ... + /// ## String::new() + /// } + /// + /// ##[derive(Debug)] + /// pub struct UriModifierInterceptor; + /// impl Interceptor for UriModifierInterceptor { + /// fn modify_before_signing( + /// &self, + /// context: &mut InterceptorContext, + /// _cfg: &mut ConfigBag, + /// ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { + /// let request = context.request_mut(); + /// let uri = format!("{}{}", base_url(), request.uri().path()); + /// *request.uri_mut() = uri.parse()?; + /// + /// Ok(()) + /// } + /// } + /// + /// let config = Config::builder() + /// .interceptor(UriModifierInterceptor) + /// .build(); + /// ## } + /// ## } + /// ``` + pub fn interceptor(mut self, interceptor: impl #{Interceptor} + 'static) -> Self { + self.push_interceptor(#{SharedInterceptor}::new(interceptor)); + self + } + + #{maybe_hide_orchestrator_code} + /// Add a [`SharedInterceptor`](#{SharedInterceptor}) that runs at specific stages of the request execution pipeline. + /// + /// Interceptors targeted at a certain stage are executed according to the pre-defined priority. + /// The SDK provides a default set of interceptors. An interceptor configured by this method + /// will run after those default interceptors. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; + /// use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext, SharedInterceptor}; + /// use aws_smithy_types::config_bag::ConfigBag; + /// use $moduleUseName::config::{Builder, Config}; + /// + /// fn base_url() -> String { + /// // ... + /// ## String::new() + /// } + /// + /// fn modify_request_uri(builder: &mut Builder) { + /// ##[derive(Debug)] + /// pub struct UriModifierInterceptor; + /// impl Interceptor for UriModifierInterceptor { + /// fn modify_before_signing( + /// &self, + /// context: &mut InterceptorContext, + /// _cfg: &mut ConfigBag, + /// ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { + /// let request = context.request_mut(); + /// let uri = format!("{}{}", base_url(), request.uri().path()); + /// *request.uri_mut() = uri.parse()?; + /// + /// Ok(()) + /// } + /// } + /// builder.push_interceptor(SharedInterceptor::new(UriModifierInterceptor)); + /// } + /// + /// let mut builder = Config::builder(); + /// modify_request_uri(&mut builder); + /// let config = builder.build(); + /// ## } + /// ## } + /// ``` + pub fn push_interceptor(&mut self, interceptor: #{SharedInterceptor}) -> &mut Self { + self.runtime_components.push_interceptor(interceptor); + self + } + + #{maybe_hide_orchestrator_code} + /// Set [`SharedInterceptor`](#{SharedInterceptor})s for the builder. + pub fn set_interceptors(&mut self, interceptors: impl IntoIterator) -> &mut Self { + self.runtime_components.set_interceptors(interceptors.into_iter()); + self + } + """, + *codegenScope, + ) + + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomization.kt new file mode 100644 index 0000000000..03c62fb049 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomization.kt @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.sdkId + +class MetadataCustomization( + private val codegenContext: ClientCodegenContext, + operation: OperationShape, +) : OperationCustomization() { + private val operationName = codegenContext.symbolProvider.toSymbol(operation).name + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope by lazy { + arrayOf( + "Metadata" to RuntimeType.operationModule(runtimeConfig).resolve("Metadata"), + ) + } + + override fun section(section: OperationSection): Writable = writable { + when (section) { + is OperationSection.AdditionalRuntimePluginConfig -> { + rustTemplate( + """ + ${section.newLayerName}.store_put(#{Metadata}::new( + ${operationName.dq()}, + ${codegenContext.serviceShape.sdkId().dq()}, + )); + """, + *codegenScope, + ) + } + + else -> {} + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt new file mode 100644 index 0000000000..38582fabcd --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt @@ -0,0 +1,39 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType + +val noAuthSchemeShapeId: ShapeId = ShapeId.from("aws.smithy.rs#NoAuth") + +private fun noAuthModule(codegenContext: ClientCodegenContext): RuntimeType = + CargoDependency.smithyRuntime(codegenContext.runtimeConfig) + .toType() + .resolve("client::auth::no_auth") + +class NoAuthDecorator : ClientCodegenDecorator { + override val name: String = "NoAuthDecorator" + override val order: Byte = 0 + + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthSchemeOptions: List, + ): List = baseAuthSchemeOptions + + AuthSchemeOption.StaticAuthSchemeOption(noAuthSchemeShapeId) { + rustTemplate( + "#{NO_AUTH_SCHEME_ID},", + "NO_AUTH_SCHEME_ID" to noAuthModule(codegenContext).resolve("NO_AUTH_SCHEME_ID"), + ) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index a52c4a81d6..8dde7f43a8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -5,74 +5,131 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.util.sdkId -class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCustomization() { +class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenContext) : ConfigCustomization() { private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val retryConfig = RuntimeType.smithyTypes(runtimeConfig).resolve("retry") private val sleepModule = RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep") private val timeoutModule = RuntimeType.smithyTypes(runtimeConfig).resolve("timeout") + private val retries = RuntimeType.smithyRuntime(runtimeConfig).resolve("client::retries") private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( - "AsyncSleep" to sleepModule.resolve("AsyncSleep"), + *preludeScope, + "ClientRateLimiter" to retries.resolve("ClientRateLimiter"), + "ClientRateLimiterPartition" to retries.resolve("ClientRateLimiterPartition"), + "debug" to RuntimeType.Tracing.resolve("debug"), "RetryConfig" to retryConfig.resolve("RetryConfig"), + "RetryMode" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryMode"), + "RetryPartition" to retries.resolve("RetryPartition"), + "SharedAsyncSleep" to sleepModule.resolve("SharedAsyncSleep"), + "SharedRetryStrategy" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::retries::SharedRetryStrategy"), + "SharedTimeSource" to RuntimeType.smithyAsync(runtimeConfig).resolve("time::SharedTimeSource"), "Sleep" to sleepModule.resolve("Sleep"), + "StandardRetryStrategy" to retries.resolve("strategy::StandardRetryStrategy"), + "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), "TimeoutConfig" to timeoutModule.resolve("TimeoutConfig"), + "TokenBucket" to retries.resolve("TokenBucket"), + "TokenBucketPartition" to retries.resolve("TokenBucketPartition"), ) override fun section(section: ServiceConfig) = writable { when (section) { - is ServiceConfig.ConfigStruct -> rustTemplate( - """ - retry_config: Option<#{RetryConfig}>, - sleep_impl: Option>, - timeout_config: Option<#{TimeoutConfig}>, - """, - *codegenScope, - ) + is ServiceConfig.ConfigStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate( + """ + retry_config: #{Option}<#{RetryConfig}>, + sleep_impl: #{Option}<#{SharedAsyncSleep}>, + timeout_config: #{Option}<#{TimeoutConfig}>, + """, + *codegenScope, + ) + } + } is ServiceConfig.ConfigImpl -> { - rustTemplate( - """ - /// Return a reference to the retry configuration contained in this config, if any. - pub fn retry_config(&self) -> Option<&#{RetryConfig}> { - self.retry_config.as_ref() - } + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Return a reference to the retry configuration contained in this config, if any. + pub fn retry_config(&self) -> #{Option}<&#{RetryConfig}> { + self.config.load::<#{RetryConfig}>() + } - /// Return a cloned Arc containing the async sleep implementation from this config, if any. - pub fn sleep_impl(&self) -> Option> { - self.sleep_impl.clone() - } + /// Return a cloned shared async sleep implementation from this config, if any. + pub fn sleep_impl(&self) -> #{Option}<#{SharedAsyncSleep}> { + self.runtime_components.sleep_impl() + } - /// Return a reference to the timeout configuration contained in this config, if any. - pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> { - self.timeout_config.as_ref() - } - """, - *codegenScope, - ) + /// Return a reference to the timeout configuration contained in this config, if any. + pub fn timeout_config(&self) -> #{Option}<&#{TimeoutConfig}> { + self.config.load::<#{TimeoutConfig}>() + } + + ##[doc(hidden)] + /// Returns a reference to the retry partition contained in this config, if any. + /// + /// WARNING: This method is unstable and may be removed at any time. Do not rely on this + /// method for anything! + pub fn retry_partition(&self) -> #{Option}<&#{RetryPartition}> { + self.config.load::<#{RetryPartition}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Return a reference to the retry configuration contained in this config, if any. + pub fn retry_config(&self) -> #{Option}<&#{RetryConfig}> { + self.retry_config.as_ref() + } + + /// Return a cloned shared async sleep implementation from this config, if any. + pub fn sleep_impl(&self) -> #{Option}<#{SharedAsyncSleep}> { + self.sleep_impl.clone() + } + + /// Return a reference to the timeout configuration contained in this config, if any. + pub fn timeout_config(&self) -> #{Option}<&#{TimeoutConfig}> { + self.timeout_config.as_ref() + } + """, + *codegenScope, + ) + } } - is ServiceConfig.BuilderStruct -> - rustTemplate( - """ - retry_config: Option<#{RetryConfig}>, - sleep_impl: Option>, - timeout_config: Option<#{TimeoutConfig}>, - """, - *codegenScope, - ) + is ServiceConfig.BuilderStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate( + """ + retry_config: #{Option}<#{RetryConfig}>, + sleep_impl: #{Option}<#{SharedAsyncSleep}>, + timeout_config: #{Option}<#{TimeoutConfig}>, + """, + *codegenScope, + ) + } + } - ServiceConfig.BuilderImpl -> + is ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Set the retry_config for the builder @@ -106,17 +163,41 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// disable_retries(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_retry_config(&mut self, retry_config: Option<#{RetryConfig}>) -> &mut Self { - self.retry_config = retry_config; - self - } + """, + *codegenScope, + ) + + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + pub fn set_retry_config(&mut self, retry_config: #{Option}<#{RetryConfig}>) -> &mut Self { + retry_config.map(|r| self.config.store_put(r)); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + pub fn set_retry_config(&mut self, retry_config: #{Option}<#{RetryConfig}>) -> &mut Self { + self.retry_config = retry_config; + self + } + """, + *codegenScope, + ) + } + + rustTemplate( + """ /// Set the sleep_impl for the builder /// /// ## Examples /// /// ```no_run - /// use $moduleUseName::config::{AsyncSleep, Sleep, Config}; + /// use $moduleUseName::config::{AsyncSleep, Config, SharedAsyncSleep, Sleep}; /// /// ##[derive(Debug)] /// pub struct ForeverSleep; @@ -127,10 +208,10 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// } /// } /// - /// let sleep_impl = std::sync::Arc::new(ForeverSleep); + /// let sleep_impl = SharedAsyncSleep::new(ForeverSleep); /// let config = Config::builder().sleep_impl(sleep_impl).build(); /// ``` - pub fn sleep_impl(mut self, sleep_impl: std::sync::Arc) -> Self { + pub fn sleep_impl(mut self, sleep_impl: #{SharedAsyncSleep}) -> Self { self.set_sleep_impl(Some(sleep_impl)); self } @@ -140,7 +221,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// ## Examples /// /// ```no_run - /// use $moduleUseName::config::{AsyncSleep, Sleep, Builder, Config}; + /// use $moduleUseName::config::{AsyncSleep, Builder, Config, SharedAsyncSleep, Sleep}; /// /// ##[derive(Debug)] /// pub struct ForeverSleep; @@ -152,7 +233,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// } /// /// fn set_never_ending_sleep_impl(builder: &mut Builder) { - /// let sleep_impl = std::sync::Arc::new(ForeverSleep); + /// let sleep_impl = SharedAsyncSleep::new(ForeverSleep); /// builder.set_sleep_impl(Some(sleep_impl)); /// } /// @@ -160,10 +241,34 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// set_never_ending_sleep_impl(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_sleep_impl(&mut self, sleep_impl: Option>) -> &mut Self { - self.sleep_impl = sleep_impl; - self - } + """, + *codegenScope, + ) + + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + pub fn set_sleep_impl(&mut self, sleep_impl: #{Option}<#{SharedAsyncSleep}>) -> &mut Self { + self.runtime_components.set_sleep_impl(sleep_impl); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + pub fn set_sleep_impl(&mut self, sleep_impl: #{Option}<#{SharedAsyncSleep}>) -> &mut Self { + self.sleep_impl = sleep_impl; + self + } + """, + *codegenScope, + ) + } + + rustTemplate( + """ /// Set the timeout_config for the builder /// @@ -204,59 +309,196 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// set_request_timeout(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_timeout_config(&mut self, timeout_config: Option<#{TimeoutConfig}>) -> &mut Self { - self.timeout_config = timeout_config; - self - } """, *codegenScope, ) - ServiceConfig.BuilderBuild -> rustTemplate( - // We call clone on sleep_impl because the field is used by - // initializing the credentials_cache field later in the build - // method of a Config builder. - // We could rearrange the order of decorators so that AwsCodegenDecorator - // runs before RequiredCustomizations, which in turns renders - // CredentialsCacheDecorator before this class, but that is a bigger - // change than adding a call to the clone method on sleep_impl. - """ - retry_config: self.retry_config, - sleep_impl: self.sleep_impl.clone(), - timeout_config: self.timeout_config, - """, - *codegenScope, - ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + pub fn set_timeout_config(&mut self, timeout_config: #{Option}<#{TimeoutConfig}>) -> &mut Self { + timeout_config.map(|t| self.config.store_put(t)); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + pub fn set_timeout_config(&mut self, timeout_config: #{Option}<#{TimeoutConfig}>) -> &mut Self { + self.timeout_config = timeout_config; + self + } + """, + *codegenScope, + ) + } + + if (runtimeMode.generateOrchestrator) { + Attribute.DocHidden.render(this) + rustTemplate( + """ + /// Set the partition for retry-related state. When clients share a retry partition, they will + /// also share things like token buckets and client rate limiters. By default, all clients + /// for the same service will share a partition. + pub fn retry_partition(mut self, retry_partition: #{RetryPartition}) -> Self { + self.set_retry_partition(Some(retry_partition)); + self + } + """, + *codegenScope, + ) + + Attribute.DocHidden.render(this) + rustTemplate( + """ + /// Set the partition for retry-related state. When clients share a retry partition, they will + /// also share things like token buckets and client rate limiters. By default, all clients + /// for the same service will share a partition. + pub fn set_retry_partition(&mut self, retry_partition: #{Option}<#{RetryPartition}>) -> &mut Self { + retry_partition.map(|r| self.config.store_put(r)); + self + } + """, + *codegenScope, + ) + } + } + + is ServiceConfig.BuilderBuild -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + if layer.load::<#{RetryConfig}>().is_none() { + layer.store_put(#{RetryConfig}::disabled()); + } + let retry_config = layer.load::<#{RetryConfig}>().expect("set to default above").clone(); + + if layer.load::<#{RetryPartition}>().is_none() { + layer.store_put(#{RetryPartition}::new("${codegenContext.serviceShape.sdkId()}")); + } + let retry_partition = layer.load::<#{RetryPartition}>().expect("set to default above").clone(); + + if retry_config.has_retry() { + #{debug}!("using retry strategy with partition '{}'", retry_partition); + } + + if retry_config.mode() == #{RetryMode}::Adaptive { + if let #{Some}(time_source) = self.runtime_components.time_source() { + let seconds_since_unix_epoch = time_source + .now() + .duration_since(#{SystemTime}::UNIX_EPOCH) + .expect("the present takes place after the UNIX_EPOCH") + .as_secs_f64(); + let client_rate_limiter_partition = #{ClientRateLimiterPartition}::new(retry_partition.clone()); + let client_rate_limiter = CLIENT_RATE_LIMITER.get_or_init(client_rate_limiter_partition, || { + #{ClientRateLimiter}::new(seconds_since_unix_epoch) + }); + layer.store_put(client_rate_limiter); + } + } + + // The token bucket is used for both standard AND adaptive retries. + let token_bucket_partition = #{TokenBucketPartition}::new(retry_partition); + let token_bucket = TOKEN_BUCKET.get_or_init(token_bucket_partition, #{TokenBucket}::default); + layer.store_put(token_bucket); + + // TODO(enableNewSmithyRuntimeCleanup): Should not need to provide a default once smithy-rs##2770 + // is resolved + if layer.load::<#{TimeoutConfig}>().is_none() { + layer.store_put(#{TimeoutConfig}::disabled()); + } + + self.runtime_components.set_retry_strategy(#{Some}( + #{SharedRetryStrategy}::new(#{StandardRetryStrategy}::new(&retry_config))) + ); + """, + *codegenScope, + ) + } else { + rustTemplate( + // We call clone on sleep_impl because the field is used by + // initializing the credentials_cache field later in the build + // method of a Config builder. + """ + retry_config: self.retry_config, + sleep_impl: self.sleep_impl.clone(), + timeout_config: self.timeout_config, + """, + *codegenScope, + ) + } + } else -> emptySection } } } -class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) { +class ResiliencyReExportCustomization(codegenContext: ClientCodegenContext) { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode + fun extras(rustCrate: RustCrate) { - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rustTemplate( - """ - pub use #{sleep}::{AsyncSleep, Sleep}; - - /// Retry configuration - /// - /// These are re-exported from `aws-smithy-types` for convenience. - pub mod retry { - pub use #{types_retry}::{RetryConfig, RetryConfigBuilder, RetryMode}; - } - /// Timeout configuration - /// - /// These are re-exported from `aws-smithy-types` for convenience. - pub mod timeout { - pub use #{timeout}::{TimeoutConfig, TimeoutConfigBuilder}; - } - """, - "types_retry" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry"), + "pub use #{sleep}::{AsyncSleep, SharedAsyncSleep, Sleep};", "sleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep"), + ) + } + rustCrate.withModule(ClientRustModule.Config.retry) { + rustTemplate( + "pub use #{types_retry}::{RetryConfig, RetryConfigBuilder, RetryMode, ReconnectMode};", + "types_retry" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry"), + ) + + if (runtimeMode.generateOrchestrator) { + rustTemplate( + "pub use #{types_retry}::RetryPartition;", + "types_retry" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::retries"), + ) + } + } + rustCrate.withModule(ClientRustModule.Config.timeout) { + rustTemplate( + "pub use #{timeout}::{TimeoutConfig, TimeoutConfigBuilder};", "timeout" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout"), ) } } } + +class ResiliencyServiceRuntimePluginCustomization(codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode + private val smithyRuntime = RuntimeType.smithyRuntime(runtimeConfig) + private val retries = smithyRuntime.resolve("client::retries") + private val codegenScope = arrayOf( + "TokenBucket" to retries.resolve("TokenBucket"), + "TokenBucketPartition" to retries.resolve("TokenBucketPartition"), + "ClientRateLimiter" to retries.resolve("ClientRateLimiter"), + "ClientRateLimiterPartition" to retries.resolve("ClientRateLimiterPartition"), + "StaticPartitionMap" to smithyRuntime.resolve("static_partition_map::StaticPartitionMap"), + ) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (runtimeMode.generateOrchestrator) { + when (section) { + is ServiceRuntimePluginSection.DeclareSingletons -> { + // TODO(enableNewSmithyRuntimeCleanup) We can use the standard library's `OnceCell` once we upgrade the + // MSRV to 1.70 + rustTemplate( + """ + static TOKEN_BUCKET: #{StaticPartitionMap}<#{TokenBucketPartition}, #{TokenBucket}> = #{StaticPartitionMap}::new(); + static CLIENT_RATE_LIMITER: #{StaticPartitionMap}<#{ClientRateLimiterPartition}, #{ClientRateLimiter}> = #{StaticPartitionMap}::new(); + """, + *codegenScope, + ) + } + + else -> emptySection + } + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt new file mode 100644 index 0000000000..7dea61de99 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt @@ -0,0 +1,166 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope + +class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val runtimeMode = codegenContext.smithyRuntimeMode + private val codegenScope = arrayOf( + *preludeScope, + "SharedTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::SharedTimeSource"), + "StaticTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::StaticTimeSource"), + "UNIX_EPOCH" to RuntimeType.std.resolve("time::UNIX_EPOCH"), + "Duration" to RuntimeType.std.resolve("time::Duration"), + ) + + override fun section(section: ServiceConfig) = + writable { + when (section) { + is ServiceConfig.ConfigStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate( + """ + pub(crate) time_source: #{SharedTimeSource}, + """, + *codegenScope, + ) + } + } + + is ServiceConfig.ConfigImpl -> { + rust("/// Return time source used for this service.") + rustBlockTemplate( + "pub fn time_source(&self) -> #{Option}<#{SharedTimeSource}>", + *codegenScope, + ) { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """self.runtime_components.time_source()""", + *codegenScope, + ) + } else { + rustTemplate("#{Some}(self.time_source.clone())", *codegenScope) + } + } + } + + is ServiceConfig.BuilderStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate( + "time_source: #{Option}<#{SharedTimeSource}>,", + *codegenScope, + ) + } + } + + ServiceConfig.BuilderImpl -> { + rustTemplate( + """ + /// Sets the time source used for this service + pub fn time_source( + mut self, + time_source: impl #{Into}<#{SharedTimeSource}>, + ) -> Self { + self.set_time_source(#{Some}(time_source.into())); + self + } + """, + *codegenScope, + ) + + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Sets the time source used for this service + pub fn set_time_source( + &mut self, + time_source: #{Option}<#{SharedTimeSource}>, + ) -> &mut Self { + self.runtime_components.set_time_source(time_source); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the time source used for this service + pub fn set_time_source( + &mut self, + time_source: #{Option}<#{SharedTimeSource}>, + ) -> &mut Self { + self.time_source = time_source; + self + } + """, + *codegenScope, + ) + } + } + + ServiceConfig.BuilderBuild -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + if self.runtime_components.time_source().is_none() { + self.runtime_components.set_time_source(#{Some}(#{Default}::default())); + } + """, + *codegenScope, + ) + } else { + rustTemplate( + "time_source: self.time_source.unwrap_or_default(),", + *codegenScope, + ) + } + } + + is ServiceConfig.DefaultForTests -> { + rustTemplate( + """ + ${section.configBuilderRef} + .set_time_source(#{Some}(#{SharedTimeSource}::new( + #{StaticTimeSource}::new(#{UNIX_EPOCH} + #{Duration}::from_secs(1234567890))) + )); + """, + *codegenScope, + ) + } + + else -> emptySection + } + } +} + +class TimeSourceOperationCustomization : OperationCustomization() { + override fun section(section: OperationSection): Writable { + return when (section) { + is OperationSection.MutateRequest -> writable { + rust( + """ + ${section.request}.properties_mut().insert(${section.config}.time_source.clone()); + """, + ) + } + + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index 6949fc8b5d..36a517bb18 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -9,19 +9,32 @@ import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.smithy.customize.CombinedCoreCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.customize.CoreCodegenDecorator -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import java.util.ServiceLoader import java.util.logging.Logger -typealias ClientProtocolMap = ProtocolMap +typealias ClientProtocolMap = ProtocolMap + +sealed interface AuthSchemeOption { + /** Auth scheme for the `StaticAuthSchemeOptionResolver` */ + data class StaticAuthSchemeOption( + val schemeShapeId: ShapeId, + val constructor: Writable, + ) : AuthSchemeOption + + class CustomResolver(/* unimplemented */) : AuthSchemeOption +} /** * [ClientCodegenDecorator] allows downstream users to customize code generation. @@ -30,7 +43,13 @@ typealias ClientProtocolMap = ProtocolMap { +interface ClientCodegenDecorator : CoreCodegenDecorator { + fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthSchemeOptions: List, + ): List = baseAuthSchemeOptions + fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -58,6 +77,22 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { * Hook to customize client construction documentation. */ fun clientConstructionDocs(codegenContext: ClientCodegenContext, baseDocs: Writable): Writable = baseDocs + + /** + * Hooks to register additional service-level runtime plugins at codegen time + */ + fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + + /** + * Hook to override the protocol test generator + */ + fun protocolTestGenerator( + codegenContext: ClientCodegenContext, + baseGenerator: ProtocolTestGenerator, + ): ProtocolTestGenerator = baseGenerator } /** @@ -66,12 +101,20 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { * This makes the actual concrete codegen simpler by not needing to deal with multiple separate decorators. */ open class CombinedClientCodegenDecorator(decorators: List) : - CombinedCoreCodegenDecorator(decorators), ClientCodegenDecorator { + CombinedCoreCodegenDecorator(decorators), ClientCodegenDecorator { override val name: String get() = "CombinedClientCodegenDecorator" override val order: Byte get() = 0 + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthSchemeOptions: List, + ): List = combineCustomizations(baseAuthSchemeOptions) { decorator, authOptions -> + decorator.authOptions(codegenContext, operationShape, authOptions) + } + override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -107,6 +150,21 @@ open class CombinedClientCodegenDecorator(decorators: List, + ): List = + combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.serviceRuntimePluginCustomizations(codegenContext, customizations) + } + + override fun protocolTestGenerator( + codegenContext: ClientCodegenContext, + baseGenerator: ProtocolTestGenerator, + ): ProtocolTestGenerator = combineCustomizations(baseGenerator) { decorator, gen -> + decorator.protocolTestGenerator(codegenContext, gen) + } + companion object { fun fromClasspath( context: PluginContext, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/NoOpEventStreamSigningDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/NoOpEventStreamSigningDecorator.kt deleted file mode 100644 index 7d924ee75a..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/NoOpEventStreamSigningDecorator.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.client.smithy.customize - -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.EventStreamSigningConfig -import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.util.hasEventStreamOperations - -/** - * The NoOpEventStreamSigningDecorator: - * - adds a `new_event_stream_signer()` method to `config` to create an Event Stream NoOp signer - */ -open class NoOpEventStreamSigningDecorator : ClientCodegenDecorator { - override val name: String = "NoOpEventStreamSigning" - override val order: Byte = Byte.MIN_VALUE - - private fun applies(codegenContext: CodegenContext, baseCustomizations: List): Boolean = - codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model) && - // and if there is no other `EventStreamSigningConfig`, apply this one - !baseCustomizations.any { it is EventStreamSigningConfig } - - override fun configCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - if (!applies(codegenContext, baseCustomizations)) { - return baseCustomizations - } - return baseCustomizations + NoOpEventStreamSigningConfig( - codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model), - codegenContext.runtimeConfig, - ) - } -} - -class NoOpEventStreamSigningConfig( - private val serviceHasEventStream: Boolean, - runtimeConfig: RuntimeConfig, -) : EventStreamSigningConfig(runtimeConfig) { - - private val codegenScope = arrayOf( - "NoOpSigner" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::NoOpSigner"), - ) - - override fun configImplSection() = renderEventStreamSignerFn { - writable { - if (serviceHasEventStream) { - rustTemplate( - """ - #{NoOpSigner}{} - """, - *codegenScope, - ) - } - } - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index 4dad5c716c..46de4940a0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -8,12 +8,20 @@ package software.amazon.smithy.rust.codegen.client.smithy.customize import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.customizations.ConnectionPoisoningRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.EndpointPrefixGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpChecksumRequiredGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpVersionListCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdempotencyTokenGenerator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.InterceptorConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.customizations.MetadataCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization +import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceCustomization +import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceOperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.smithy.RustCrate @@ -21,8 +29,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLints import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyErrorTypes import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyPrimitives -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization +import software.amazon.smithy.rust.codegen.core.util.letIf val TestUtilFeature = Feature("test-util", false, listOf()) @@ -40,17 +48,29 @@ class RequiredCustomizations : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List = - baseCustomizations + + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + MetadataCustomization(codegenContext, operation) + } + IdempotencyTokenGenerator(codegenContext, operation) + EndpointPrefixGenerator(codegenContext, operation) + HttpChecksumRequiredGenerator(codegenContext, operation) + - HttpVersionListCustomization(codegenContext, operation) + HttpVersionListCustomization(codegenContext, operation) + + TimeSourceOperationCustomization() override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations + ResiliencyConfigCustomization(codegenContext) + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + baseCustomizations + + ResiliencyConfigCustomization(codegenContext) + + InterceptorConfigCustomization(codegenContext) + + TimeSourceCustomization(codegenContext) + } else { + baseCustomizations + + ResiliencyConfigCustomization(codegenContext) + + TimeSourceCustomization(codegenContext) + } override fun libRsCustomizations( codegenContext: ClientCodegenContext, @@ -65,7 +85,7 @@ class RequiredCustomizations : ClientCodegenDecorator { rustCrate.mergeFeature(TestUtilFeature) // Re-export resiliency types - ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(rustCrate) + ResiliencyReExportCustomization(codegenContext).extras(rustCrate) rustCrate.withModule(ClientRustModule.Primitives) { pubUseSmithyPrimitives(codegenContext, codegenContext.model)(this) @@ -80,4 +100,15 @@ class RequiredCustomizations : ClientCodegenDecorator { } } } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + baseCustomizations + + ResiliencyServiceRuntimePluginCustomization(codegenContext) + + ConnectionPoisoningRuntimePluginCustomization(codegenContext) + } else { + baseCustomizations + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt index 05be47f0f5..db842def8d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt @@ -11,19 +11,22 @@ import software.amazon.smithy.model.shapes.ShapeType import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rulesengine.traits.ClientContextParamDefinition import software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.join import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase /** @@ -32,10 +35,11 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase * This handles injecting parameters like `s3::Accelerate` or `s3::ForcePathStyle`. The resulting parameters become * setters on the config builder object. */ -class ClientContextConfigCustomization(ctx: CodegenContext) : ConfigCustomization() { +class ClientContextConfigCustomization(ctx: ClientCodegenContext) : ConfigCustomization() { + private val runtimeConfig = ctx.runtimeConfig private val configParams = ctx.serviceShape.getTrait()?.parameters.orEmpty().toList() - .map { (key, value) -> fromClientParam(key, value, ctx.symbolProvider) } - private val decorators = configParams.map { standardConfigParam(it) } + .map { (key, value) -> fromClientParam(key, value, ctx.symbolProvider, runtimeConfig) } + private val decorators = configParams.map { standardConfigParam(it, ctx) } companion object { fun toSymbol(shapeType: ShapeType, symbolProvider: RustSymbolProvider): Symbol = @@ -51,10 +55,13 @@ class ClientContextConfigCustomization(ctx: CodegenContext) : ConfigCustomizatio name: String, definition: ClientContextParamDefinition, symbolProvider: RustSymbolProvider, + runtimeConfig: RuntimeConfig, ): ConfigParam { + val inner = toSymbol(definition.type, symbolProvider) return ConfigParam( RustReservedWords.escapeIfNeeded(name.toSnakeCase()), - toSymbol(definition.type, symbolProvider), + inner, + configParamNewtype(RustReservedWords.escapeIfNeeded(name.toPascalCase()), inner, runtimeConfig), definition.documentation.orNull()?.let { writable { docs(it) } }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index d6bb2cccc7..b1871c6ffc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -13,52 +13,84 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope /** * Customization which injects an Endpoints 2.0 Endpoint Resolver into the service config struct */ internal class EndpointConfigCustomization( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, private val typesGenerator: EndpointTypesGenerator, ) : ConfigCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val moduleUseName = codegenContext.moduleUseName() + private val runtimeMode = codegenContext.smithyRuntimeMode private val types = Types(runtimeConfig) + private val codegenScope = arrayOf( + *preludeScope, + "DefaultEndpointResolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), + "Endpoint" to RuntimeType.smithyHttp(runtimeConfig).resolve("endpoint::Endpoint"), + "OldSharedEndpointResolver" to types.sharedEndpointResolver, + "Params" to typesGenerator.paramsStruct(), + "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), + "SharedEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::endpoint::SharedEndpointResolver"), + "SmithyResolver" to types.resolveEndpoint, + ) + override fun section(section: ServiceConfig): Writable { return writable { + val sharedEndpointResolver = "#{OldSharedEndpointResolver}<#{Params}>" val resolverTrait = "#{SmithyResolver}<#{Params}>" - val codegenScope = arrayOf( - "SmithyResolver" to types.resolveEndpoint, - "Params" to typesGenerator.paramsStruct(), - ) when (section) { - is ServiceConfig.ConfigStruct -> rustTemplate( - "pub (crate) endpoint_resolver: std::sync::Arc,", - *codegenScope, - ) + is ServiceConfig.ConfigStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate( + "pub (crate) endpoint_resolver: $sharedEndpointResolver,", + *codegenScope, + ) + } + } - is ServiceConfig.ConfigImpl -> - rustTemplate( - """ - /// Returns the endpoint resolver. - pub fn endpoint_resolver(&self) -> std::sync::Arc { - self.endpoint_resolver.clone() - } - """, - *codegenScope, - ) + is ServiceConfig.ConfigImpl -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Returns the endpoint resolver. + pub fn endpoint_resolver(&self) -> #{SharedEndpointResolver} { + self.runtime_components.endpoint_resolver().expect("resolver defaulted if not set") + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns the endpoint resolver. + pub fn endpoint_resolver(&self) -> $sharedEndpointResolver { + self.endpoint_resolver.clone() + } + """, + *codegenScope, + ) + } + } - is ServiceConfig.BuilderStruct -> - rustTemplate( - "endpoint_resolver: Option>,", - *codegenScope, - ) + is ServiceConfig.BuilderStruct -> { + if (runtimeMode.generateMiddleware) { + rustTemplate( + "endpoint_resolver: #{Option}<$sharedEndpointResolver>,", + *codegenScope, + ) + } + } ServiceConfig.BuilderImpl -> { // if there are no rules, we don't generate a default resolver—we need to also suppress those docs. val defaultResolverDocs = if (typesGenerator.defaultResolver() != null) { + val endpointModule = ClientRustModule.endpoint(codegenContext).fullyQualifiedPath() + .replace("crate::", "$moduleUseName::") """ /// /// When unset, the client will used a generated endpoint resolver based on the endpoint resolution @@ -67,8 +99,9 @@ internal class EndpointConfigCustomization( /// ## Examples /// ```no_run /// use aws_smithy_http::endpoint; - /// use $moduleUseName::endpoint::{Params as EndpointParams, DefaultResolver}; + /// use $endpointModule::{Params as EndpointParams, DefaultResolver}; /// /// Endpoint resolver which adds a prefix to the generated endpoint + /// ##[derive(Debug)] /// struct PrefixResolver { /// base_resolver: DefaultResolver, /// prefix: String @@ -93,12 +126,50 @@ internal class EndpointConfigCustomization( } else { "" } + if (codegenContext.settings.codegenConfig.includeEndpointUrlConfig) { + rustTemplate( + """ + /// Set the endpoint URL to use when making requests. + /// + /// Note: setting an endpoint URL will replace any endpoint resolver that has been set. + /// + /// ## Panics + /// Panics if an invalid URL is given. + pub fn endpoint_url(mut self, endpoint_url: impl #{Into}<#{String}>) -> Self { + self.set_endpoint_url(#{Some}(endpoint_url.into())); + self + } + + /// Set the endpoint URL to use when making requests. + /// + /// Note: setting an endpoint URL will replace any endpoint resolver that has been set. + /// + /// ## Panics + /// Panics if an invalid URL is given. + pub fn set_endpoint_url(&mut self, endpoint_url: #{Option}<#{String}>) -> &mut Self { + ##[allow(deprecated)] + self.set_endpoint_resolver( + endpoint_url.map(|url| { + #{OldSharedEndpointResolver}::new( + #{Endpoint}::immutable(url).expect("invalid endpoint URL") + ) + }) + ); + self + } + """, + *codegenScope, + ) + } rustTemplate( """ /// Sets the endpoint resolver to use when making requests. + /// + /// Note: setting an endpoint resolver will replace any endpoint URL that has been set. + /// $defaultResolverDocs pub fn endpoint_resolver(mut self, endpoint_resolver: impl $resolverTrait + 'static) -> Self { - self.endpoint_resolver = Some(std::sync::Arc::new(endpoint_resolver) as _); + self.set_endpoint_resolver(#{Some}(#{OldSharedEndpointResolver}::new(endpoint_resolver))); self } @@ -106,51 +177,57 @@ internal class EndpointConfigCustomization( /// /// When unset, the client will used a generated endpoint resolver based on the endpoint resolution /// rules for `$moduleUseName`. - pub fn set_endpoint_resolver(&mut self, endpoint_resolver: Option>) -> &mut Self { - self.endpoint_resolver = endpoint_resolver; - self - } """, *codegenScope, ) - } - ServiceConfig.BuilderBuild -> { - val defaultResolver = typesGenerator.defaultResolver() - if (defaultResolver != null) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ - endpoint_resolver: self.endpoint_resolver.unwrap_or_else(|| - std::sync::Arc::new(#{DefaultResolver}::new()) - ), + pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self { + self.config.store_or_unset(endpoint_resolver); + self + } """, *codegenScope, - "DefaultResolver" to defaultResolver, ) } else { - val alwaysFailsResolver = - RuntimeType.forInlineFun("MissingResolver", ClientRustModule.Endpoint) { - rustTemplate( - """ - pub(crate) struct MissingResolver; - impl #{ResolveEndpoint} for MissingResolver { - fn resolve_endpoint(&self, _params: &T) -> #{Result} { - Err(#{ResolveEndpointError}::message("an endpoint resolver must be provided.")) - } - } - """, - "ResolveEndpoint" to types.resolveEndpoint, - "ResolveEndpointError" to types.resolveEndpointError, - "Result" to types.smithyHttpEndpointModule.resolve("Result"), - ) + rustTemplate( + """ + pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self { + self.endpoint_resolver = endpoint_resolver; + self } - // To keep this diff under control, rather than `.expect` here, insert a resolver that will - // always fail. In the future, this will be changed to an `expect()` + """, + *codegenScope, + ) + } + } + + ServiceConfig.BuilderBuild -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + "#{set_endpoint_resolver}(&mut resolver);", + "set_endpoint_resolver" to setEndpointResolverFn(), + ) + } else { rustTemplate( """ - endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||std::sync::Arc::new(#{FailingResolver})), + endpoint_resolver: self.endpoint_resolver.unwrap_or_else(|| + #{OldSharedEndpointResolver}::new(#{DefaultResolver}::new()) + ), """, - "FailingResolver" to alwaysFailsResolver, + *codegenScope, + "DefaultResolver" to defaultResolver(), + ) + } + } + + is ServiceConfig.OperationConfigOverride -> { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + "#{set_endpoint_resolver}(&mut resolver);", + "set_endpoint_resolver" to setEndpointResolverFn(), ) } } @@ -159,4 +236,58 @@ internal class EndpointConfigCustomization( } } } + + private fun defaultResolver(): RuntimeType { + // For now, fallback to a default endpoint resolver that always fails. In the future, + // the endpoint resolver will be required (so that it can be unwrapped). + return typesGenerator.defaultResolver() ?: RuntimeType.forInlineFun( + "MissingResolver", + ClientRustModule.endpoint(codegenContext), + ) { + rustTemplate( + """ + ##[derive(Debug)] + pub(crate) struct MissingResolver; + impl MissingResolver { + pub(crate) fn new() -> Self { Self } + } + impl #{ResolveEndpoint} for MissingResolver { + fn resolve_endpoint(&self, _params: &T) -> #{Result} { + Err(#{ResolveEndpointError}::message("an endpoint resolver must be provided.")) + } + } + """, + "ResolveEndpoint" to types.resolveEndpoint, + "ResolveEndpointError" to types.resolveEndpointError, + "Result" to types.smithyHttpEndpointModule.resolve("Result"), + ) + } + } + + private fun setEndpointResolverFn(): RuntimeType = RuntimeType.forInlineFun("set_endpoint_resolver", ClientRustModule.config) { + // TODO(enableNewSmithyRuntimeCleanup): Simplify the endpoint resolvers + rustTemplate( + """ + fn set_endpoint_resolver(resolver: &mut #{Resolver}<'_>) { + let endpoint_resolver = if resolver.is_initial() { + Some(resolver.resolve_config::<#{OldSharedEndpointResolver}<#{Params}>>().cloned().unwrap_or_else(|| + #{OldSharedEndpointResolver}::new(#{DefaultResolver}::new()) + )) + } else if resolver.is_latest_set::<#{OldSharedEndpointResolver}<#{Params}>>() { + resolver.resolve_config::<#{OldSharedEndpointResolver}<#{Params}>>().cloned() + } else { + None + }; + if let Some(endpoint_resolver) = endpoint_resolver { + let shared = #{SharedEndpointResolver}::new( + #{DefaultEndpointResolver}::<#{Params}>::new(endpoint_resolver) + ); + resolver.runtime_components_mut().set_endpoint_resolver(#{Some}(shared)); + } + } + """, + *codegenScope, + "DefaultResolver" to defaultResolver(), + ) + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt new file mode 100644 index 0000000000..c3d136659d --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt @@ -0,0 +1,52 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.endpoint + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.letIf + +/** + * Decorator that injects operation-level interceptors that configure an endpoint parameters builder + * with operation specific information, e.g. a bucket name. + * + * Whenever a setter needs to be called on the endpoint parameters builder with operation specific information, + * this decorator must be used. + */ +class EndpointParamsDecorator : ClientCodegenDecorator { + override val name: String get() = "EndpointParamsDecorator" + override val order: Byte get() = 0 + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(EndpointParametersCustomization(codegenContext, operation)) + } +} + +private class EndpointParametersCustomization( + private val codegenContext: ClientCodegenContext, + private val operation: OperationShape, +) : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + val symbolProvider = codegenContext.symbolProvider + val operationName = symbolProvider.toSymbol(operation).name + if (section is OperationSection.AdditionalInterceptors) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rust("${operationName}EndpointParamsInterceptor") + } + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt index 2f6e496e69..889b741ebb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt @@ -40,9 +40,10 @@ class EndpointTypesGenerator( } } - fun paramsStruct(): RuntimeType = EndpointParamsGenerator(params).paramsStruct() + fun paramsStruct(): RuntimeType = EndpointParamsGenerator(codegenContext, params).paramsStruct() + fun paramsBuilder(): RuntimeType = EndpointParamsGenerator(codegenContext, params).paramsBuilder() fun defaultResolver(): RuntimeType? = - rules?.let { EndpointResolverGenerator(stdlib, runtimeConfig).defaultEndpointResolver(it) } + rules?.let { EndpointResolverGenerator(codegenContext, stdlib).defaultEndpointResolver(it) } fun testGenerator(): Writable = defaultResolver()?.let { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt index 8aa3990850..351d205813 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt @@ -18,16 +18,17 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.CustomRuntimeFunction import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsGenerator -import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointTests +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.endpointTestsModule import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rulesgen.SmithyEndpointsStdLib +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.orNull @@ -133,8 +134,8 @@ class EndpointsDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val generator = EndpointTypesGenerator.fromContext(codegenContext) - rustCrate.withModule(ClientRustModule.Endpoint) { - withInlineModule(EndpointTests, rustCrate.moduleDocProvider) { + rustCrate.withModule(ClientRustModule.endpoint(codegenContext)) { + withInlineModule(endpointTestsModule(codegenContext), rustCrate.moduleDocProvider) { generator.testGenerator()(this) } } @@ -153,6 +154,7 @@ class EndpointsDecorator : ClientCodegenDecorator { * .build(); * ``` */ + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization class InjectEndpointInMakeOperation( private val ctx: ClientCodegenContext, private val typesGenerator: EndpointTypesGenerator, @@ -165,18 +167,21 @@ class EndpointsDecorator : ClientCodegenDecorator { override fun section(section: OperationSection): Writable { val codegenScope = arrayOf( + *RuntimeType.preludeScope, "Params" to typesGenerator.paramsStruct(), + "ResolveEndpoint" to types.resolveEndpoint, "ResolveEndpointError" to types.resolveEndpointError, ) return when (section) { is OperationSection.MutateInput -> writable { rustTemplate( """ + use #{ResolveEndpoint}; let params_result = #{Params}::builder()#{builderFields:W}.build() - .map_err(|err|#{ResolveEndpointError}::from_source("could not construct endpoint parameters", err)); + .map_err(|err| #{ResolveEndpointError}::from_source("could not construct endpoint parameters", err)); let (endpoint_result, params) = match params_result { - Ok(params) => (${section.config}.endpoint_resolver.resolve_endpoint(¶ms), Some(params)), - Err(e) => (Err(e), None) + #{Ok}(params) => (${section.config}.endpoint_resolver.resolve_endpoint(¶ms), #{Some}(params)), + #{Err}(e) => (#{Err}(e), #{None}) }; """, "builderFields" to builderFields(typesGenerator.params, section), @@ -187,7 +192,7 @@ class EndpointsDecorator : ClientCodegenDecorator { is OperationSection.MutateRequest -> writable { // insert the endpoint the bag rustTemplate("${section.request}.properties_mut().insert(endpoint_result);") - rustTemplate("""if let Some(params) = params { ${section.request}.properties_mut().insert(params); }""") + rustTemplate("""if let #{Some}(params) = params { ${section.request}.properties_mut().insert(params); }""", *codegenScope) } else -> emptySection @@ -198,8 +203,8 @@ class EndpointsDecorator : ClientCodegenDecorator { val node = this return writable { when (node) { - is StringNode -> rust("Some(${node.value.dq()}.to_string())") - is BooleanNode -> rust("Some(${node.value})") + is StringNode -> rustTemplate("#{Some}(${node.value.dq()}.to_string())", *RuntimeType.preludeScope) + is BooleanNode -> rustTemplate("#{Some}(${node.value})", *RuntimeType.preludeScope) else -> PANIC("unsupported default value: $node") } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt index ed283bc6de..bed14f40ae 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt @@ -48,8 +48,11 @@ class Types(runtimeConfig: RuntimeConfig) { private val smithyTypesEndpointModule = RuntimeType.smithyTypes(runtimeConfig).resolve("endpoint") val smithyHttpEndpointModule = RuntimeType.smithyHttp(runtimeConfig).resolve("endpoint") val resolveEndpoint = smithyHttpEndpointModule.resolve("ResolveEndpoint") + val sharedEndpointResolver = smithyHttpEndpointModule.resolve("SharedEndpointResolver") val smithyEndpoint = smithyTypesEndpointModule.resolve("Endpoint") val resolveEndpointError = smithyHttpEndpointModule.resolve("ResolveEndpointError") + + fun toArray() = arrayOf("ResolveEndpointError" to resolveEndpointError, "Endpoint" to smithyEndpoint) } /** diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt index 286f34443d..779a4eb8d2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators import software.amazon.smithy.rulesengine.language.eval.Value import software.amazon.smithy.rulesengine.language.syntax.Identifier import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.endpoint.memberName import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName @@ -28,6 +29,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType @@ -36,12 +38,12 @@ import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.orNull // internals contains the actual resolver function -val EndpointImpl = RustModule.private("internals", parent = ClientRustModule.Endpoint) +fun endpointImplModule(codegenContext: ClientCodegenContext) = RustModule.private("internals", parent = ClientRustModule.endpoint(codegenContext)) -val EndpointTests = RustModule.new( +fun endpointTestsModule(codegenContext: ClientCodegenContext) = RustModule.new( "test", visibility = Visibility.PRIVATE, - parent = ClientRustModule.Endpoint, + parent = ClientRustModule.endpoint(codegenContext), inline = true, documentationOverride = "", ).cfgTest() @@ -107,22 +109,24 @@ val EndpointStdLib = RustModule.private("endpoint_lib") * ``` */ -internal class EndpointParamsGenerator(private val parameters: Parameters) { - +internal class EndpointParamsGenerator( + private val codegenContext: ClientCodegenContext, + private val parameters: Parameters, +) { companion object { fun memberName(parameterName: String) = Identifier.of(parameterName).rustName() fun setterName(parameterName: String) = "set_${memberName(parameterName)}" } - fun paramsStruct(): RuntimeType = RuntimeType.forInlineFun("Params", ClientRustModule.Endpoint) { + fun paramsStruct(): RuntimeType = RuntimeType.forInlineFun("Params", ClientRustModule.endpoint(codegenContext)) { generateEndpointsStruct(this) } - private fun endpointsBuilder(): RuntimeType = RuntimeType.forInlineFun("ParamsBuilder", ClientRustModule.Endpoint) { + internal fun paramsBuilder(): RuntimeType = RuntimeType.forInlineFun("ParamsBuilder", ClientRustModule.endpoint(codegenContext)) { generateEndpointParamsBuilder(this) } - private fun paramsError(): RuntimeType = RuntimeType.forInlineFun("InvalidParams", ClientRustModule.Endpoint) { + private fun paramsError(): RuntimeType = RuntimeType.forInlineFun("InvalidParams", ClientRustModule.endpoint(codegenContext)) { rust( """ /// An error that occurred during endpoint resolution @@ -182,7 +186,7 @@ internal class EndpointParamsGenerator(private val parameters: Parameters) { #{Builder}::default() } """, - "Builder" to endpointsBuilder(), + "Builder" to paramsBuilder(), ) parameters.toList().forEach { parameter -> val name = parameter.memberName() @@ -232,7 +236,8 @@ internal class EndpointParamsGenerator(private val parameters: Parameters) { rustWriter.rustBlock("impl ParamsBuilder") { docs("Consume this builder, creating [`Params`].") rustBlockTemplate( - "pub fn build(self) -> Result<#{Params}, #{ParamsError}>", + "pub fn build(self) -> #{Result}<#{Params}, #{ParamsError}>", + *preludeScope, "Params" to paramsStruct(), "ParamsError" to paramsError(), ) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt new file mode 100644 index 0000000000..2c46cf4bba --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -0,0 +1,182 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators + +import software.amazon.smithy.model.node.BooleanNode +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.node.StringNode +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters +import software.amazon.smithy.rulesengine.traits.ContextIndex +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.ClientContextConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName +import software.amazon.smithy.rust.codegen.client.smithy.generators.EndpointTraitBindings +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.loadFromConfigBag +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.util.PANIC +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.inputShape +import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.core.util.toPascalCase + +class EndpointParamsInterceptorGenerator( + private val codegenContext: ClientCodegenContext, +) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val endpointTypesGenerator = EndpointTypesGenerator.fromContext(codegenContext) + private val codegenScope = codegenContext.runtimeConfig.let { rc -> + val endpointTypesGenerator = EndpointTypesGenerator.fromContext(codegenContext) + val runtimeApi = CargoDependency.smithyRuntimeApi(rc).toType() + val interceptors = runtimeApi.resolve("client::interceptors") + val orchestrator = runtimeApi.resolve("client::orchestrator") + arrayOf( + *preludeScope, + "BoxError" to RuntimeType.boxError(rc), + "ConfigBag" to RuntimeType.configBag(rc), + "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), + "EndpointResolverParams" to runtimeApi.resolve("client::endpoint::EndpointResolverParams"), + "HttpRequest" to orchestrator.resolve("HttpRequest"), + "HttpResponse" to orchestrator.resolve("HttpResponse"), + "Interceptor" to RuntimeType.interceptor(rc), + "InterceptorContext" to RuntimeType.interceptorContext(rc), + "BeforeSerializationInterceptorContextRef" to RuntimeType.beforeSerializationInterceptorContextRef(rc), + "Input" to interceptors.resolve("context::Input"), + "Output" to interceptors.resolve("context::Output"), + "Error" to interceptors.resolve("context::Error"), + "InterceptorError" to interceptors.resolve("error::InterceptorError"), + "Params" to endpointTypesGenerator.paramsStruct(), + ) + } + + fun render(writer: RustWriter, operationShape: OperationShape) { + val operationName = symbolProvider.toSymbol(operationShape).name + val operationInput = symbolProvider.toSymbol(operationShape.inputShape(model)) + val interceptorName = "${operationName}EndpointParamsInterceptor" + writer.rustTemplate( + """ + ##[derive(Debug)] + struct $interceptorName; + + impl #{Interceptor} for $interceptorName { + fn name(&self) -> &'static str { + ${interceptorName.dq()} + } + + fn read_before_execution( + &self, + context: &#{BeforeSerializationInterceptorContextRef}<'_, #{Input}, #{Output}, #{Error}>, + cfg: &mut #{ConfigBag}, + ) -> #{Result}<(), #{BoxError}> { + let _input = context.input() + .downcast_ref::<${operationInput.name}>() + .ok_or("failed to downcast to ${operationInput.name}")?; + + #{endpoint_prefix:W} + + let params = #{Params}::builder() + #{param_setters} + .build() + .map_err(|err| #{ContextAttachedError}::new("endpoint params could not be built", err))?; + cfg.interceptor_state().store_put(#{EndpointResolverParams}::new(params)); + #{Ok}(()) + } + } + """, + *codegenScope, + "endpoint_prefix" to endpointPrefix(operationShape), + "param_setters" to paramSetters(operationShape, endpointTypesGenerator.params), + ) + } + + private fun paramSetters(operationShape: OperationShape, params: Parameters) = writable { + val idx = ContextIndex.of(codegenContext.model) + val memberParams = idx.getContextParams(operationShape).toList().sortedBy { it.first.memberName } + val builtInParams = params.toList().filter { it.isBuiltIn } + // first load builtins and their defaults + builtInParams.forEach { param -> + val config = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + "cfg" + } else { + "_config" + } + endpointTypesGenerator.builtInFor(param, config)?.also { defaultValue -> + rust(".set_${param.name.rustName()}(#W)", defaultValue) + } + } + + idx.getClientContextParams(codegenContext.serviceShape).orNull()?.parameters?.forEach { (name, param) -> + val setterName = EndpointParamsGenerator.setterName(name) + val inner = ClientContextConfigCustomization.toSymbol(param.type, symbolProvider) + val newtype = configParamNewtype(name.toPascalCase(), inner, codegenContext.runtimeConfig) + rustTemplate( + ".$setterName(cfg.#{load_from_service_config_layer})", + "load_from_service_config_layer" to loadFromConfigBag(inner.name, newtype), + ) + } + + idx.getStaticContextParams(operationShape).orNull()?.parameters?.forEach { (name, param) -> + val setterName = EndpointParamsGenerator.setterName(name) + val value = param.value.toWritable() + rust(".$setterName(#W)", value) + } + + // lastly, allow these to be overridden by members + memberParams.forEach { (memberShape, param) -> + val memberName = codegenContext.symbolProvider.toMemberName(memberShape) + rust( + ".${EndpointParamsGenerator.setterName(param.name)}(_input.$memberName.clone())", + ) + } + } + + private fun Node.toWritable(): Writable { + val node = this + return writable { + when (node) { + is StringNode -> rust("Some(${node.value.dq()}.to_string())") + is BooleanNode -> rust("Some(${node.value})") + else -> PANIC("unsupported default value: $node") + } + } + } + + private fun endpointPrefix(operationShape: OperationShape): Writable = writable { + operationShape.getTrait(EndpointTrait::class.java).map { epTrait -> + val endpointTraitBindings = EndpointTraitBindings( + codegenContext.model, + symbolProvider, + codegenContext.runtimeConfig, + operationShape, + epTrait, + ) + withBlockTemplate( + "let endpoint_prefix = ", + """.map_err(|err| #{ContextAttachedError}::new("endpoint prefix could not be built", err))?;""", + *codegenScope, + ) { + endpointTraitBindings.render( + this, + "_input", + codegenContext.smithyRuntimeMode, + ) + } + rust("cfg.interceptor_state().store_put(endpoint_prefix);") + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt index d85282f16e..a823720571 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rulesengine.language.syntax.fn.IsSet import software.amazon.smithy.rulesengine.language.syntax.rule.Condition import software.amazon.smithy.rulesengine.language.syntax.rule.Rule import software.amazon.smithy.rulesengine.language.visit.RuleValueVisitor +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Context import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types @@ -33,7 +34,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.toType import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.orNull @@ -119,7 +119,11 @@ class FunctionRegistry(private val functions: List) { * */ -internal class EndpointResolverGenerator(stdlib: List, runtimeConfig: RuntimeConfig) { +internal class EndpointResolverGenerator( + private val codegenContext: ClientCodegenContext, + stdlib: List, +) { + private val runtimeConfig = codegenContext.runtimeConfig private val registry: FunctionRegistry = FunctionRegistry(stdlib) private val types = Types(runtimeConfig) private val codegenScope = arrayOf( @@ -164,11 +168,11 @@ internal class EndpointResolverGenerator(stdlib: List, ru // Now that we rendered the rules once (and then threw it away) we can see what functions we actually used! val fnsUsed = registry.fnsUsed() - return RuntimeType.forInlineFun("DefaultResolver", ClientRustModule.Endpoint) { + return RuntimeType.forInlineFun("DefaultResolver", ClientRustModule.endpoint(codegenContext)) { rustTemplate( """ /// The default endpoint resolver - ##[derive(Default)] + ##[derive(Debug, Default)] pub struct DefaultResolver { #{custom_fields:W} } @@ -190,7 +194,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru """, "custom_fields" to fnsUsed.mapNotNull { it.structField() }.join(","), "custom_fields_init" to fnsUsed.mapNotNull { it.structFieldInit() }.join(","), - "Params" to EndpointParamsGenerator(endpointRuleSet.parameters).paramsStruct(), + "Params" to EndpointParamsGenerator(codegenContext, endpointRuleSet.parameters).paramsStruct(), "additional_args" to fnsUsed.mapNotNull { it.additionalArgsInvocation("self") }.join(","), "resolver_fn" to resolverFn(endpointRuleSet, fnsUsed), *codegenScope, @@ -202,7 +206,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru endpointRuleSet: EndpointRuleSet, fnsUsed: List, ): RuntimeType { - return RuntimeType.forInlineFun("resolve_endpoint", EndpointImpl) { + return RuntimeType.forInlineFun("resolve_endpoint", endpointImplModule(codegenContext)) { Attribute(allow(allowLintsForResolver)).render(this) rustTemplate( """ @@ -212,7 +216,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru """, *codegenScope, - "Params" to EndpointParamsGenerator(endpointRuleSet.parameters).paramsStruct(), + "Params" to EndpointParamsGenerator(codegenContext, endpointRuleSet.parameters).paramsStruct(), "additional_args" to fnsUsed.mapNotNull { it.additionalArgsSignature() }.join(","), "body" to resolverFnBody(endpointRuleSet), ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt index c4d6efc327..c5b415b2a6 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt @@ -10,10 +10,11 @@ import software.amazon.smithy.rulesengine.language.syntax.Identifier import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters import software.amazon.smithy.rulesengine.traits.EndpointTestCase import software.amazon.smithy.rulesengine.traits.ExpectedEndpoint +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName -import software.amazon.smithy.rust.codegen.client.smithy.generators.clientInstantiator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.escape @@ -22,7 +23,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq @@ -34,8 +34,7 @@ internal class EndpointTestGenerator( private val resolverType: RuntimeType, private val params: Parameters, private val endpointCustomizations: List, - codegenContext: CodegenContext, - + codegenContext: ClientCodegenContext, ) { private val runtimeConfig = codegenContext.runtimeConfig private val serviceShape = codegenContext.serviceShape @@ -50,7 +49,7 @@ internal class EndpointTestGenerator( "capture_request" to RuntimeType.captureRequest(runtimeConfig), ) - private val instantiator = clientInstantiator(codegenContext) + private val instantiator = ClientInstantiator(codegenContext) private fun EndpointTestCase.docs(): Writable { val self = this diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt index d1b87591cb..64f6d9446b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumMemberModel @@ -60,16 +61,17 @@ data class InfallibleEnumType( } override fun implFromStr(context: EnumGeneratorContext): Writable = writable { - rust( + rustTemplate( """ - impl std::str::FromStr for ${context.enumName} { - type Err = std::convert::Infallible; + impl ::std::str::FromStr for ${context.enumName} { + type Err = ::std::convert::Infallible; - fn from_str(s: &str) -> std::result::Result { - Ok(${context.enumName}::from(s)) + fn from_str(s: &str) -> #{Result}::Err> { + #{Ok}(${context.enumName}::from(s)) } } """, + *preludeScope, ) } @@ -98,7 +100,7 @@ data class InfallibleEnumType( """.trimIndent(), ) context.enumMeta.render(this) - rust("struct $UnknownVariantValue(pub(crate) String);") + rustTemplate("struct $UnknownVariantValue(pub(crate) #{String});", *preludeScope) rustBlock("impl $UnknownVariantValue") { // The generated as_str is not pub as we need to prevent users from calling it on this opaque struct. rustBlock("pub(crate) fn as_str(&self) -> &str") { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt index b74079cc21..a065fe3b96 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt @@ -6,8 +6,14 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerator +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable @@ -29,11 +35,27 @@ class ClientBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiat override fun doesSetterTakeInOption(memberShape: MemberShape): Boolean = true } -fun clientInstantiator(codegenContext: CodegenContext) = - Instantiator( - codegenContext.symbolProvider, - codegenContext.model, - codegenContext.runtimeConfig, - ClientBuilderKindBehavior(codegenContext), - ::enumFromStringFn, - ) +class ClientInstantiator(private val codegenContext: ClientCodegenContext) : Instantiator( + codegenContext.symbolProvider, + codegenContext.model, + codegenContext.runtimeConfig, + ClientBuilderKindBehavior(codegenContext), + ::enumFromStringFn, +) { + fun renderFluentCall( + writer: RustWriter, + clientName: String, + operationShape: OperationShape, + inputShape: StructureShape, + data: Node, + headers: Map = mapOf(), + ctx: Ctx = Ctx(), + ) { + val operationBuilderName = + FluentClientGenerator.clientOperationFnName(operationShape, codegenContext.symbolProvider) + + writer.rust("$clientName.$operationBuilderName()") + + renderStructureMembers(writer, inputShape, data as ObjectNode, headers, ctx) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt new file mode 100644 index 0000000000..102400e511 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt @@ -0,0 +1,104 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate + +class ClientRuntimeTypesReExportGenerator( + private val codegenContext: ClientCodegenContext, + private val rustCrate: RustCrate, +) { + fun render() { + if (!codegenContext.smithyRuntimeMode.generateOrchestrator) { + return + } + + val rc = codegenContext.runtimeConfig + val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(rc) + + rustCrate.withModule(ClientRustModule.config) { + rustTemplate( + """ + pub use #{ConfigBag}; + pub use #{Interceptor}; + pub use #{SharedInterceptor}; + """, + "ConfigBag" to RuntimeType.configBag(rc), + "Interceptor" to RuntimeType.interceptor(rc), + "SharedInterceptor" to RuntimeType.sharedInterceptor(rc), + ) + + if (codegenContext.enableUserConfigurableRuntimePlugins) { + rustTemplate( + """ + pub use #{runtime_plugin}::{RuntimePlugin, SharedRuntimePlugin}; + pub use #{config_bag}::FrozenLayer; + """, + "runtime_plugin" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin"), + "config_bag" to RuntimeType.smithyTypes(rc).resolve("config_bag"), + ) + } + } + rustCrate.withModule(ClientRustModule.endpoint(codegenContext)) { + rustTemplate( + """ + pub use #{ResolveEndpoint}; + pub use #{SharedEndpointResolver}; + """, + "ResolveEndpoint" to RuntimeType.smithyHttp(rc).resolve("endpoint::ResolveEndpoint"), + "SharedEndpointResolver" to RuntimeType.smithyHttp(rc).resolve("endpoint::SharedEndpointResolver"), + ) + } + rustCrate.withModule(ClientRustModule.Config.retry) { + rustTemplate( + """ + pub use #{ClassifyRetry}; + pub use #{RetryReason}; + pub use #{ShouldAttempt}; + """, + "ClassifyRetry" to smithyRuntimeApi.resolve("client::retries::ClassifyRetry"), + "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), + "ShouldAttempt" to smithyRuntimeApi.resolve("client::retries::ShouldAttempt"), + ) + } + rustCrate.withModule(ClientRustModule.Config.interceptors) { + rustTemplate( + """ + pub use #{AfterDeserializationInterceptorContextRef}; + pub use #{BeforeDeserializationInterceptorContextMut}; + pub use #{BeforeDeserializationInterceptorContextRef}; + pub use #{BeforeSerializationInterceptorContextMut}; + pub use #{BeforeSerializationInterceptorContextRef}; + pub use #{BeforeTransmitInterceptorContextMut}; + pub use #{BeforeTransmitInterceptorContextRef}; + pub use #{FinalizerInterceptorContextMut}; + pub use #{FinalizerInterceptorContextRef}; + pub use #{InterceptorContext}; + """, + "AfterDeserializationInterceptorContextRef" to RuntimeType.afterDeserializationInterceptorContextRef(rc), + "BeforeDeserializationInterceptorContextMut" to RuntimeType.beforeDeserializationInterceptorContextMut(rc), + "BeforeDeserializationInterceptorContextRef" to RuntimeType.beforeDeserializationInterceptorContextRef(rc), + "BeforeSerializationInterceptorContextMut" to RuntimeType.beforeSerializationInterceptorContextMut(rc), + "BeforeSerializationInterceptorContextRef" to RuntimeType.beforeSerializationInterceptorContextRef(rc), + "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(rc), + "BeforeTransmitInterceptorContextRef" to RuntimeType.beforeTransmitInterceptorContextRef(rc), + "FinalizerInterceptorContextMut" to RuntimeType.finalizerInterceptorContextMut(rc), + "FinalizerInterceptorContextRef" to RuntimeType.finalizerInterceptorContextRef(rc), + "InterceptorContext" to RuntimeType.interceptorContext(rc), + ) + } + rustCrate.withModule(ClientRustModule.Error) { + rustTemplate( + "pub use #{BoxError};", + "BoxError" to RuntimeType.boxError(rc), + ) + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt new file mode 100644 index 0000000000..0e595065f9 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations + +class ConfigOverrideRuntimePluginGenerator( + codegenContext: ClientCodegenContext, +) { + private val moduleUseName = codegenContext.moduleUseName() + private val codegenScope = codegenContext.runtimeConfig.let { rc -> + val runtimeApi = RuntimeType.smithyRuntimeApi(rc) + val smithyTypes = RuntimeType.smithyTypes(rc) + arrayOf( + *RuntimeType.preludeScope, + "Cow" to RuntimeType.Cow, + "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), + "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "Resolver" to RuntimeType.smithyRuntime(rc).resolve("client::config_override::Resolver"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(rc), + "RuntimePlugin" to RuntimeType.runtimePlugin(rc), + ) + } + + fun render(writer: RustWriter, customizations: List) { + writer.rustTemplate( + """ + /// A plugin that enables configuration for a single operation invocation + /// + /// The `config` method will return a `FrozenLayer` by storing values from `config_override`. + /// In the case of default values requested, they will be obtained from `client_config`. + ##[derive(Debug)] + pub(crate) struct ConfigOverrideRuntimePlugin { + pub(crate) config: #{FrozenLayer}, + pub(crate) components: #{RuntimeComponentsBuilder}, + } + + impl ConfigOverrideRuntimePlugin { + pub(crate) fn new( + config_override: Builder, + initial_config: #{FrozenLayer}, + initial_components: &#{RuntimeComponentsBuilder} + ) -> Self { + let mut layer = config_override.config; + let mut components = config_override.runtime_components; + let mut resolver = #{Resolver}::overrid(initial_config, initial_components, &mut layer, &mut components); + + #{config} + + let _ = resolver; + Self { + config: #{Layer}::from(layer) + .with_name("$moduleUseName::config::ConfigOverrideRuntimePlugin").freeze(), + components, + } + } + } + + impl #{RuntimePlugin} for ConfigOverrideRuntimePlugin { + fn config(&self) -> #{Option}<#{FrozenLayer}> { + Some(self.config.clone()) + } + + fn runtime_components(&self) -> #{Cow}<'_, #{RuntimeComponentsBuilder}> { + #{Cow}::Borrowed(&self.components) + } + } + """, + *codegenScope, + "config" to writable { + writeCustomizations( + customizations, + ServiceConfig.OperationConfigOverride("layer"), + ) + }, + ) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt index c0f17d46ad..1741a08035 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.smithy.generators.http.rustFormatString import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -44,7 +45,12 @@ class EndpointTraitBindings( * * The returned expression is a `Result` */ - fun render(writer: RustWriter, input: String) { + fun render( + writer: RustWriter, + input: String, + smithyRuntimeMode: SmithyRuntimeMode, + generateValidation: Boolean = true, + ) { // the Rust format pattern to make the endpoint prefix e.g. "{}.foo" val formatLiteral = endpointTrait.prefixFormatString() if (endpointTrait.hostPrefix.labels.isEmpty()) { @@ -67,17 +73,30 @@ class EndpointTraitBindings( // NOTE: this is dead code until we start respecting @required rust("let $field = &$input.$field;") } - rustTemplate( - """ - if $field.is_empty() { - return Err(#{invalidFieldError:W}) + if (generateValidation) { + val contents = if (smithyRuntimeMode.generateOrchestrator) { + // TODO(enableNewSmithyRuntimeCleanup): Remove the allow attribute once all places need .into method + """ + if $field.is_empty() { + ##[allow(clippy::useless_conversion)] + return Err(#{invalidFieldError:W}.into()) + } + """ + } else { + """ + if $field.is_empty() { + return Err(#{invalidFieldError:W}) + } + """ } - """, - "invalidFieldError" to OperationBuildError(runtimeConfig).invalidField( - field, - "$field was unset or empty but must be set as part of the endpoint prefix", - ), - ) + rustTemplate( + contents, + "invalidFieldError" to OperationBuildError(runtimeConfig).invalidField( + field, + "$field was unset or empty but must be set as part of the endpoint prefix", + ), + ) + } "${label.content} = $field" } rustTemplate( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt index 2c752814be..138c7eb123 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType @@ -72,17 +73,18 @@ class NestedAccessorGenerator(private val codegenContext: CodegenContext) { "" } if (path.isEmpty()) { - rust("Some(input)") + rustTemplate("#{Some}(input)", *preludeScope) } else { val head = path.first() if (symbolProvider.toSymbol(head).isOptional()) { - rust( + rustTemplate( """ let input = match ${ref}input.${symbolProvider.toMemberName(head)} { - None => return None, - Some(t) => t + #{None} => return #{None}, + #{Some}(t) => t }; """, + *preludeScope, ) } else { rust("let input = input.${symbolProvider.toMemberName(head)};") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt new file mode 100644 index 0000000000..90552c3659 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt @@ -0,0 +1,191 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol + +sealed class OperationSection(name: String) : Section(name) { + abstract val customizations: List + + /** Write custom code into the `impl` block of this operation */ + data class OperationImplBlock(override val customizations: List) : + OperationSection("OperationImplBlock") + + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization hook when cleaning up middleware + /** Write additional functions inside the Input's impl block */ + @Deprecated("customization for middleware; won't be used in the orchestrator impl") + data class InputImpl( + override val customizations: List, + val operationShape: OperationShape, + val inputShape: StructureShape, + val protocol: Protocol, + ) : OperationSection("InputImpl") + + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization hook when cleaning up middleware + @Deprecated("customization for middleware; won't be used in the orchestrator impl") + data class MutateInput( + override val customizations: List, + val input: String, + val config: String, + ) : OperationSection("MutateInput") + + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization hook when cleaning up middleware + /** Write custom code into the block that builds an operation + * + * [request]: Name of the variable holding the `aws_smithy_http::Request` + * [config]: Name of the variable holding the service config. + * + * */ + @Deprecated("customization for middleware; won't be used in the orchestrator impl") + data class MutateRequest( + override val customizations: List, + val request: String, + val config: String, + ) : OperationSection("Feature") + + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization hook when cleaning up middleware + @Deprecated("customization for middleware; won't be used in the orchestrator impl") + data class FinalizeOperation( + override val customizations: List, + val operation: String, + val config: String, + ) : OperationSection("Finalize") + + data class MutateOutput( + override val customizations: List, + val operationShape: OperationShape, + /** Name of the response headers map (for referring to it in Rust code) */ + val responseHeadersName: String, + + // TODO(enableNewSmithyRuntimeCleanup): Remove this flag when switching to the orchestrator + /** Whether the property bag exists in this context */ + val propertyBagAvailable: Boolean, + ) : OperationSection("MutateOutput") + + /** + * Allows for adding additional properties to the `extras` field on the + * `aws_smithy_types::error::ErrorMetadata`. + */ + data class PopulateErrorMetadataExtras( + override val customizations: List, + /** Name of the generic error builder (for referring to it in Rust code) */ + val builderName: String, + /** Name of the response status (for referring to it in Rust code) */ + val responseStatusName: String, + /** Name of the response headers map (for referring to it in Rust code) */ + val responseHeadersName: String, + ) : OperationSection("PopulateErrorMetadataExtras") + + /** + * Hook to add custom code right before the response is parsed. + */ + data class BeforeParseResponse( + override val customizations: List, + val responseName: String, + ) : OperationSection("BeforeParseResponse") + + /** + * Hook for adding additional things to config inside operation runtime plugins. + */ + data class AdditionalRuntimePluginConfig( + override val customizations: List, + val newLayerName: String, + val operationShape: OperationShape, + ) : OperationSection("AdditionalRuntimePluginConfig") + + data class AdditionalInterceptors( + override val customizations: List, + val operationShape: OperationShape, + ) : OperationSection("AdditionalInterceptors") { + fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { + val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) + writer.rustTemplate( + """ + .with_interceptor( + #{SharedInterceptor}::new( + #{interceptor} + ) as _ + ) + """, + "interceptor" to interceptor, + "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), + ) + } + } + + /** + * Hook for adding retry classifiers to an operation's `RetryClassifiers` bundle. + * + * Should emit 1+ lines of code that look like the following: + * ```rust + * .with_classifier(AwsErrorCodeClassifier::new()) + * .with_classifier(HttpStatusCodeClassifier::new()) + * ``` + */ + data class RetryClassifier( + override val customizations: List, + val configBagName: String, + val operationShape: OperationShape, + ) : OperationSection("RetryClassifier") + + /** + * Hook for adding supporting types for operation-specific runtime plugins. + * Examples include various operation-specific types (retry classifiers, config bag types, etc.) + */ + data class RuntimePluginSupportingTypes( + override val customizations: List, + val configBagName: String, + val operationShape: OperationShape, + ) : OperationSection("RuntimePluginSupportingTypes") + + /** + * Hook for adding additional runtime plugins to an operation. + */ + data class AdditionalRuntimePlugins( + override val customizations: List, + val operationShape: OperationShape, + ) : OperationSection("AdditionalRuntimePlugins") { + fun addServiceRuntimePlugin(writer: RustWriter, plugin: Writable) { + writer.rustTemplate(".with_service_plugin(#{plugin})", "plugin" to plugin) + } + + fun addOperationRuntimePlugin(writer: RustWriter, plugin: Writable) { + writer.rustTemplate(".with_operation_plugin(#{plugin})", "plugin" to plugin) + } + } +} + +abstract class OperationCustomization : NamedCustomization() { + // TODO(enableNewSmithyRuntimeCleanup): Delete this when cleaning up middleware + @Deprecated("property for middleware; won't be used in the orchestrator impl") + open fun retryType(): RuntimeType? = null + + // TODO(enableNewSmithyRuntimeCleanup): Delete this when cleaning up middleware + /** + * Does `make_operation` consume the self parameter? + * + * This is required for things like idempotency tokens where the operation can only be sent once + * and an idempotency token will mutate the request. + */ + @Deprecated("property for middleware; won't be used in the orchestrator impl") + open fun consumesSelf(): Boolean = false + + // TODO(enableNewSmithyRuntimeCleanup): Delete this when cleaning up middleware + /** + * Does `make_operation` mutate the self parameter? + */ + @Deprecated("property for middleware; won't be used in the orchestrator impl") + open fun mutSelf(): Boolean = false +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt new file mode 100644 index 0000000000..10f52767f4 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -0,0 +1,227 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.RequestSerializerGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ResponseDeserializerGenerator +import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock +import software.amazon.smithy.rust.codegen.core.rustlang.isNotEmpty +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.inputShape +import software.amazon.smithy.rust.codegen.core.util.outputShape +import software.amazon.smithy.rust.codegen.core.util.sdkId + +open class OperationGenerator( + private val codegenContext: ClientCodegenContext, + private val protocol: Protocol, + /** + * Operations generate a `make_operation(&config)` method to build a `aws_smithy_http::Operation` that can be dispatched + * This is the serializer side of request dispatch + */ + // TODO(enableNewSmithyRuntimeCleanup): Remove the `makeOperationGenerator` + private val makeOperationGenerator: MakeOperationGenerator, + private val bodyGenerator: ProtocolPayloadGenerator, + // TODO(enableNewSmithyRuntimeCleanup): Remove the `traitGenerator` + private val traitGenerator: HttpBoundProtocolTraitImplGenerator, +) { + private val model = codegenContext.model + private val runtimeConfig = codegenContext.runtimeConfig + private val symbolProvider = codegenContext.symbolProvider + + /** + * Render the operation struct and its supporting code. + */ + fun renderOperation( + operationWriter: RustWriter, + // TODO(enableNewSmithyRuntimeCleanup): Remove the `inputWriter` since `make_operation` generation is going away + inputWriter: RustWriter, + operationShape: OperationShape, + codegenDecorator: ClientCodegenDecorator, + ) { + val operationCustomizations = codegenDecorator.operationCustomizations(codegenContext, operationShape, emptyList()) + val inputShape = operationShape.inputShape(model) + + // impl OperationInputShape { ... } + inputWriter.implBlock(symbolProvider.toSymbol(inputShape)) { + writeCustomizations( + operationCustomizations, + OperationSection.InputImpl(operationCustomizations, operationShape, inputShape, protocol), + ) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + makeOperationGenerator.generateMakeOperation(this, operationShape, operationCustomizations) + } + } + + renderOperationStruct( + operationWriter, + operationShape, + codegenDecorator.authOptions(codegenContext, operationShape, emptyList()), + operationCustomizations, + ) + } + + private fun renderOperationStruct( + operationWriter: RustWriter, + operationShape: OperationShape, + authSchemeOptions: List, + operationCustomizations: List, + ) { + val operationName = symbolProvider.toSymbol(operationShape).name + + // pub struct Operation { ... } + operationWriter.rust( + """ + /// Orchestration and serialization glue logic for `$operationName`. + """, + ) + Attribute(derive(RuntimeType.Clone, RuntimeType.Default, RuntimeType.Debug)).render(operationWriter) + Attribute.NonExhaustive.render(operationWriter) + Attribute.DocHidden.render(operationWriter) + operationWriter.rust("pub struct $operationName;") + operationWriter.implBlock(symbolProvider.toSymbol(operationShape)) { + Attribute.DocHidden.render(operationWriter) + rustBlock("pub fn new() -> Self") { + rust("Self") + } + + val outputType = symbolProvider.toSymbol(operationShape.outputShape(model)) + val errorType = symbolProvider.symbolForOperationError(operationShape) + val codegenScope = arrayOf( + *preludeScope, + "Arc" to RuntimeType.Arc, + "ConcreteInput" to symbolProvider.toSymbol(operationShape.inputShape(model)), + "Input" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Input"), + "Operation" to symbolProvider.toSymbol(operationShape), + "OperationError" to errorType, + "OperationOutput" to outputType, + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + ) + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + val additionalPlugins = writable { + writeCustomizations( + operationCustomizations, + OperationSection.AdditionalRuntimePlugins(operationCustomizations, operationShape), + ) + } + rustTemplate( + """ + pub(crate) async fn orchestrate( + runtime_plugins: &#{RuntimePlugins}, + input: #{ConcreteInput}, + ) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + let map_err = |err: #{SdkError}<#{Error}, #{HttpResponse}>| { + err.map_service_error(|err| { + err.downcast::<#{OperationError}>().expect("correct error type") + }) + }; + let context = Self::orchestrate_with_stop_point(runtime_plugins, input, #{StopPoint}::None) + .await + .map_err(map_err)?; + let output = context.finalize().map_err(map_err)?; + #{Ok}(output.downcast::<#{OperationOutput}>().expect("correct output type")) + } + + pub(crate) async fn orchestrate_with_stop_point( + runtime_plugins: &#{RuntimePlugins}, + input: #{ConcreteInput}, + stop_point: #{StopPoint}, + ) -> #{Result}<#{InterceptorContext}, #{SdkError}<#{Error}, #{HttpResponse}>> { + let input = #{Input}::erase(input); + #{invoke_with_stop_point}( + ${codegenContext.serviceShape.sdkId().dq()}, + ${operationName.dq()}, + input, + runtime_plugins, + stop_point + ).await + } + + pub(crate) fn operation_runtime_plugins( + client_runtime_plugins: #{RuntimePlugins}, + client_config: &crate::config::Config, + config_override: #{Option}, + ) -> #{RuntimePlugins} { + let mut runtime_plugins = client_runtime_plugins.with_operation_plugin(Self::new()); + #{additional_runtime_plugins} + if let #{Some}(config_override) = config_override { + for plugin in config_override.runtime_plugins.iter().cloned() { + runtime_plugins = runtime_plugins.with_operation_plugin(plugin); + } + runtime_plugins = runtime_plugins.with_operation_plugin( + crate::config::ConfigOverrideRuntimePlugin::new(config_override, client_config.config.clone(), &client_config.runtime_components) + ); + } + runtime_plugins + } + """, + *codegenScope, + "Error" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Error"), + "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), + "OrchestratorError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::error::OrchestratorError"), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), + "StopPoint" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::StopPoint"), + "invoke_with_stop_point" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke_with_stop_point"), + "additional_runtime_plugins" to writable { + if (additionalPlugins.isNotEmpty()) { + rustTemplate( + """ + runtime_plugins = runtime_plugins + #{additional_runtime_plugins}; + """, + "additional_runtime_plugins" to additionalPlugins, + ) + } + }, + ) + } + + writeCustomizations(operationCustomizations, OperationSection.OperationImplBlock(operationCustomizations)) + } + + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + traitGenerator.generateTraitImpls(operationWriter, operationShape, operationCustomizations) + } + + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + OperationRuntimePluginGenerator(codegenContext).render( + operationWriter, + operationShape, + operationName, + authSchemeOptions, + operationCustomizations, + ) + + ResponseDeserializerGenerator(codegenContext, protocol) + .render(operationWriter, operationShape, operationCustomizations) + RequestSerializerGenerator(codegenContext, protocol, bodyGenerator) + .render(operationWriter, operationShape) + + EndpointParamsInterceptorGenerator(codegenContext) + .render(operationWriter, operationShape) + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index fb0a7f6e17..73d6756072 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -5,39 +5,168 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators +import software.amazon.smithy.model.knowledge.ServiceIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customizations.noAuthSchemeShapeId +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import java.util.logging.Logger /** * Generates operation-level runtime plugins */ class OperationRuntimePluginGenerator( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, ) { + private val logger: Logger = Logger.getLogger(javaClass.name) private val codegenScope = codegenContext.runtimeConfig.let { rc -> + val runtimeApi = RuntimeType.smithyRuntimeApi(rc) + val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( - "BoxError" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to RuntimeType.smithyRuntimeApi(rc).resolve("config_bag::ConfigBag"), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::ConfigBagAccessors"), - "RuntimePlugin" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin::RuntimePlugin"), + *preludeScope, + "AuthSchemeOptionResolverParams" to runtimeApi.resolve("client::auth::AuthSchemeOptionResolverParams"), + "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), + "Cow" to RuntimeType.Cow, + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(codegenContext.runtimeConfig), + "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), + "SharedAuthSchemeOptionResolver" to runtimeApi.resolve("client::auth::SharedAuthSchemeOptionResolver"), + "SharedRequestSerializer" to runtimeApi.resolve("client::ser_de::SharedRequestSerializer"), + "SharedResponseDeserializer" to runtimeApi.resolve("client::ser_de::SharedResponseDeserializer"), + "StaticAuthSchemeOptionResolver" to runtimeApi.resolve("client::auth::static_resolver::StaticAuthSchemeOptionResolver"), + "StaticAuthSchemeOptionResolverParams" to runtimeApi.resolve("client::auth::static_resolver::StaticAuthSchemeOptionResolverParams"), ) } - fun render(writer: RustWriter, operationStructName: String) { + fun render( + writer: RustWriter, + operationShape: OperationShape, + operationStructName: String, + authSchemeOptions: List, + customizations: List, + ) { writer.rustTemplate( """ impl #{RuntimePlugin} for $operationStructName { - fn configure(&self, cfg: &mut #{ConfigBag}) -> Result<(), #{BoxError}> { - use #{ConfigBagAccessors} as _; - cfg.set_request_serializer(${operationStructName}RequestSerializer); - cfg.set_response_deserializer(${operationStructName}ResponseDeserializer); - Ok(()) + fn config(&self) -> #{Option}<#{FrozenLayer}> { + let mut cfg = #{Layer}::new(${operationShape.id.name.dq()}); + + cfg.store_put(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); + cfg.store_put(#{SharedResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); + + ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} + cfg.store_put(#{AuthSchemeOptionResolverParams}::new(#{StaticAuthSchemeOptionResolverParams}::new())); + + #{additional_config} + + #{Some}(cfg.freeze()) + } + + fn runtime_components(&self) -> #{Cow}<'_, #{RuntimeComponentsBuilder}> { + // Retry classifiers are operation-specific because they need to downcast operation-specific error types. + let retry_classifiers = #{RetryClassifiers}::new() + #{retry_classifier_customizations}; + + #{Cow}::Owned( + #{RuntimeComponentsBuilder}::new(${operationShape.id.name.dq()}) + .with_retry_classifiers(#{Some}(retry_classifiers)) + #{auth_options} + #{interceptors} + ) } } + + #{runtime_plugin_supporting_types} """, *codegenScope, + *preludeScope, + "auth_options" to generateAuthOptions(operationShape, authSchemeOptions), + "additional_config" to writable { + writeCustomizations( + customizations, + OperationSection.AdditionalRuntimePluginConfig( + customizations, + newLayerName = "cfg", + operationShape, + ), + ) + }, + "retry_classifier_customizations" to writable { + writeCustomizations( + customizations, + OperationSection.RetryClassifier(customizations, "cfg", operationShape), + ) + }, + "runtime_plugin_supporting_types" to writable { + writeCustomizations( + customizations, + OperationSection.RuntimePluginSupportingTypes(customizations, "cfg", operationShape), + ) + }, + "interceptors" to writable { + writeCustomizations( + customizations, + OperationSection.AdditionalInterceptors(customizations, operationShape), + ) + }, ) } + + private fun generateAuthOptions( + operationShape: OperationShape, + authSchemeOptions: List, + ): Writable = writable { + if (authSchemeOptions.any { it is AuthSchemeOption.CustomResolver }) { + throw IllegalStateException("AuthSchemeOption.CustomResolver is unimplemented") + } else { + val authOptionsMap = authSchemeOptions.associate { + val option = it as AuthSchemeOption.StaticAuthSchemeOption + option.schemeShapeId to option + } + withBlockTemplate( + """ + .with_auth_scheme_option_resolver(#{Some}( + #{SharedAuthSchemeOptionResolver}::new( + #{StaticAuthSchemeOptionResolver}::new(vec![ + """, + "]))))", + *codegenScope, + ) { + var noSupportedAuthSchemes = true + val authSchemes = ServiceIndex.of(codegenContext.model) + .getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape) + for (schemeShapeId in authSchemes.keys) { + val authOption = authOptionsMap[schemeShapeId] + if (authOption != null) { + authOption.constructor(this) + noSupportedAuthSchemes = false + } else { + logger.warning( + "No auth scheme implementation available for $schemeShapeId. " + + "The generated client will not attempt to use this auth scheme.", + ) + } + } + if (operationShape.hasTrait() || noSupportedAuthSchemes) { + val authOption = authOptionsMap[noAuthSchemeShapeId] + ?: throw IllegalStateException("Missing 'no auth' implementation. This is a codegen bug.") + authOption.constructor(this) + } + } + } + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index a20a33ecb0..f68aed2dec 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -16,11 +16,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.render -import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.findMemberWithTrait @@ -86,6 +86,7 @@ class PaginatorGenerator private constructor( ) private val codegenScope = arrayOf( + *preludeScope, "generics" to generics.decl, "bounds" to generics.bounds, "page_size_setter" to pageSizeSetter(), @@ -104,6 +105,7 @@ class PaginatorGenerator private constructor( "Builder" to symbolProvider.symbolForBuilder(operation.inputShape(model)), // SDK Types + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse"), "SdkError" to RuntimeType.sdkError(runtimeConfig), "client" to RuntimeType.smithyClient(runtimeConfig), "fn_stream" to RuntimeType.smithyAsync(runtimeConfig).resolve("future::fn_stream"), @@ -158,31 +160,23 @@ class PaginatorGenerator private constructor( /// Create the pagination stream /// /// _Note:_ No requests will be dispatched until the stream is used (eg. with [`.next().await`](tokio_stream::StreamExt::next)). - pub fn send(self) -> impl #{Stream}>> + Unpin + pub fn send(self) -> impl #{Stream} + #{Unpin} #{send_bounds:W} { // Move individual fields out of self for the borrow checker let builder = self.builder; let handle = self.handle; - #{fn_stream}::FnStream::new(move |tx| Box::pin(async move { + #{runtime_plugin_init} + #{fn_stream}::FnStream::new(move |tx| #{Box}::pin(async move { // Build the input for the first time. If required fields are missing, this is where we'll produce an early error. let mut input = match builder.build().map_err(#{SdkError}::construction_failure) { - Ok(input) => input, - Err(e) => { let _ = tx.send(Err(e)).await; return; } + #{Ok}(input) => input, + #{Err}(e) => { let _ = tx.send(#{Err}(e)).await; return; } }; loop { - let op = match input.make_operation(&handle.conf) - .await - .map_err(#{SdkError}::construction_failure) { - Ok(op) => op, - Err(e) => { - let _ = tx.send(Err(e)).await; - return; - } - }; - let resp = handle.client.call(op).await; + let resp = #{orchestrate}; // If the input member is None or it was an error let done = match resp { - Ok(ref resp) => { + #{Ok}(ref resp) => { let new_token = #{output_token}(resp); let is_empty = new_token.map(|token| token.is_empty()).unwrap_or(true); if !is_empty && new_token == input.$inputTokenMember.as_ref() && self.stop_on_duplicate_token { @@ -192,7 +186,7 @@ class PaginatorGenerator private constructor( is_empty } }, - Err(_) => true, + #{Err}(_) => true, }; if tx.send(resp).await.is_err() { // receiving end was dropped @@ -209,6 +203,54 @@ class PaginatorGenerator private constructor( *codegenScope, "items_fn" to itemsFn(), "output_token" to outputTokenLens, + "item_type" to writable { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate("#{Result}<#{Output}, #{SdkError}<#{Error}>>", *codegenScope) + } else { + rustTemplate("#{Result}<#{Output}, #{SdkError}<#{Error}, #{HttpResponse}>>", *codegenScope) + } + }, + "orchestrate" to writable { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + { + let op = match input.make_operation(&handle.conf) + .await + .map_err(#{SdkError}::construction_failure) { + #{Ok}(op) => op, + #{Err}(e) => { + let _ = tx.send(#{Err}(e)).await; + return; + } + }; + handle.client.call(op).await + } + """, + *codegenScope, + ) + } else { + rustTemplate( + "#{operation}::orchestrate(&runtime_plugins, input.clone()).await", + *codegenScope, + ) + } + }, + "runtime_plugin_init" to writable { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + """ + let runtime_plugins = #{operation}::operation_runtime_plugins( + handle.runtime_plugins.clone(), + &handle.conf, + #{None}, + ); + """, + *codegenScope, + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), + ) + } + }, ) } @@ -263,7 +305,7 @@ class PaginatorGenerator private constructor( /// _Note: No requests will be dispatched until the stream is used (eg. with [`.next().await`](tokio_stream::StreamExt::next))._ /// /// To read the entirety of the paginator, use [`.collect::, _>()`](tokio_stream::StreamExt::collect). - pub fn send(self) -> impl #{Stream}>> + Unpin + pub fn send(self) -> impl #{Stream} + #{Unpin} #{send_bounds:W} { #{fn_stream}::TryFlatMap::new(self.0.send()).flat_map(|page| #{extract_items}(page).unwrap_or_default().into_iter()) } @@ -274,6 +316,13 @@ class PaginatorGenerator private constructor( outputShape, paginationInfo.itemsMemberPath, ), + "item_type" to writable { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate("#{Result}<${itemType()}, #{SdkError}<#{Error}>>", *codegenScope) + } else { + rustTemplate("#{Result}<${itemType()}, #{SdkError}<#{Error}, #{HttpResponse}>>", *codegenScope) + } + }, *codegenScope, ) } @@ -284,16 +333,17 @@ class PaginatorGenerator private constructor( val memberName = symbolProvider.toMemberName(it) val pageSizeT = symbolProvider.toSymbol(it).rustType().stripOuter().render(true) - rust( + rustTemplate( """ /// Set the page size /// /// _Note: this method will override any previously set value for `$memberName`_ pub fn page_size(mut self, limit: $pageSizeT) -> Self { - self.builder.$memberName = Some(limit); + self.builder.$memberName = #{Some}(limit); self } """, + *preludeScope, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndex.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndex.kt new file mode 100644 index 0000000000..15e8d5361c --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndex.kt @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.KnowledgeIndex +import software.amazon.smithy.model.selector.Selector +import software.amazon.smithy.model.shapes.OperationShape + +class SensitiveIndex(model: Model) : KnowledgeIndex { + private val sensitiveInputSelector = Selector.parse("operation:test(-[input]-> ~> [trait|sensitive])") + private val sensitiveOutputSelector = Selector.parse("operation:test(-[output]-> ~> [trait|sensitive])") + private val sensitiveInputs = sensitiveInputSelector.select(model).map { it.id }.toSet() + private val sensitiveOutputs = sensitiveOutputSelector.select(model).map { it.id }.toSet() + + fun hasSensitiveInput(operationShape: OperationShape): Boolean = sensitiveInputs.contains(operationShape.id) + fun hasSensitiveOutput(operationShape: OperationShape): Boolean = sensitiveOutputs.contains(operationShape.id) + + companion object { + fun of(model: Model): SensitiveIndex { + return model.getKnowledge(SensitiveIndex::class.java) { + SensitiveIndex(it) + } + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index b28a329fc4..01cecc49f3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfigGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ServiceErrorGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -22,31 +23,39 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate */ class ServiceGenerator( private val rustCrate: RustCrate, - private val clientCodegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, private val decorator: ClientCodegenDecorator, ) { - private val index = TopDownIndex.of(clientCodegenContext.model) + private val index = TopDownIndex.of(codegenContext.model) /** * Render Service-specific code. Code will end up in different files via `useShapeWriter`. See `SymbolVisitor.kt` * which assigns a symbol location to each shape. */ fun render() { - val operations = index.getContainedOperations(clientCodegenContext.serviceShape).sortedBy { it.id } + val operations = index.getContainedOperations(codegenContext.serviceShape).sortedBy { it.id } ServiceErrorGenerator( - clientCodegenContext, + codegenContext, operations, - decorator.errorCustomizations(clientCodegenContext, emptyList()), + decorator.errorCustomizations(codegenContext, emptyList()), ).render(rustCrate) - rustCrate.withModule(ClientRustModule.Config) { - ServiceConfigGenerator.withBaseBehavior( - clientCodegenContext, - extraCustomizations = decorator.configCustomizations(clientCodegenContext, listOf()), - ).render(this) + rustCrate.withModule(ClientRustModule.config) { + val serviceConfigGenerator = ServiceConfigGenerator.withBaseBehavior( + codegenContext, + extraCustomizations = decorator.configCustomizations(codegenContext, listOf()), + ) + serviceConfigGenerator.render(this) - if (clientCodegenContext.settings.codegenConfig.enableNewSmithyRuntime) { - ServiceRuntimePluginGenerator(clientCodegenContext).render(this) + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + // Enable users to opt in to the test-utils in the runtime crate + rustCrate.mergeFeature(TestUtilFeature.copy(deps = listOf("aws-smithy-runtime/test-util"))) + + ServiceRuntimePluginGenerator(codegenContext) + .render(this, decorator.serviceRuntimePluginCustomizations(codegenContext, emptyList())) + + ConfigOverrideRuntimePluginGenerator(codegenContext) + .render(this, decorator.configCustomizations(codegenContext, listOf())) } } @@ -54,5 +63,7 @@ class ServiceGenerator( Attribute.DocInline.render(this) write("pub use config::Config;") } + + ClientRuntimeTypesReExportGenerator(codegenContext, rustCrate).render() } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 5c35eed98b..86a2d4d63d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -7,59 +7,150 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.isNotEmpty +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.util.dq + +sealed class ServiceRuntimePluginSection(name: String) : Section(name) { + /** + * Hook for declaring singletons that store cross-operation state. + * + * Examples include token buckets, ID generators, etc. + */ + class DeclareSingletons : ServiceRuntimePluginSection("DeclareSingletons") + + /** + * Hook for adding additional things to config inside service runtime plugins. + */ + data class AdditionalConfig(val newLayerName: String, val serviceConfigName: String) : ServiceRuntimePluginSection("AdditionalConfig") { + /** Adds a value to the config bag */ + fun putConfigValue(writer: RustWriter, value: Writable) { + writer.rust("$newLayerName.store_put(#T);", value) + } + } + + data class RegisterRuntimeComponents(val serviceConfigName: String) : ServiceRuntimePluginSection("RegisterRuntimeComponents") { + /** Generates the code to register an interceptor */ + fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { + writer.rustTemplate( + """ + runtime_components.push_interceptor(#{SharedInterceptor}::new(#{interceptor}) as _); + """, + "interceptor" to interceptor, + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::SharedInterceptor"), + ) + } + + fun registerAuthScheme(writer: RustWriter, authScheme: Writable) { + writer.rustTemplate( + """ + runtime_components.push_auth_scheme(#{auth_scheme}); + """, + "auth_scheme" to authScheme, + ) + } + + fun registerIdentityResolver(writer: RustWriter, identityResolver: Writable) { + writer.rustTemplate( + """ + runtime_components.push_identity_resolver(#{identity_resolver}); + """, + "identity_resolver" to identityResolver, + ) + } + } +} +typealias ServiceRuntimePluginCustomization = NamedCustomization /** * Generates the service-level runtime plugin */ class ServiceRuntimePluginGenerator( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, ) { private val codegenScope = codegenContext.runtimeConfig.let { rc -> + val runtimeApi = RuntimeType.smithyRuntimeApi(rc) + val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( - "BoxError" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin::BoxError"), - "NeverRetryStrategy" to RuntimeType.smithyRuntimeApi(rc).resolve("client::retries::NeverRetryStrategy"), - "ConfigBag" to RuntimeType.smithyRuntimeApi(rc).resolve("config_bag::ConfigBag"), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::ConfigBagAccessors"), - "TestConnection" to RuntimeType.smithyRuntimeApi(rc).resolve("client::connections::test_connection::TestConnection"), - "RuntimePlugin" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin::RuntimePlugin"), - "StaticUriEndpointResolver" to RuntimeType.smithyRuntimeApi(rc).resolve("client::endpoints::StaticUriEndpointResolver"), - "StubAuthOptionResolver" to RuntimeType.smithyRuntimeApi(rc).resolve("client::auth::option_resolver::StubAuthOptionResolver"), - "StubAuthOptionResolverParams" to RuntimeType.smithyRuntimeApi(rc).resolve("client::auth::option_resolver::StubAuthOptionResolverParams"), - "AuthOptionResolverParams" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::AuthOptionResolverParams"), - "IdentityResolvers" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::IdentityResolvers"), + *preludeScope, + "Arc" to RuntimeType.Arc, + "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), + "Cow" to RuntimeType.Cow, + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(rc), + "RuntimePlugin" to RuntimeType.runtimePlugin(rc), ) } - fun render(writer: RustWriter) { + fun render( + writer: RustWriter, + customizations: List, + ) { + val additionalConfig = writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg", "_service_config")) + } writer.rustTemplate( """ + ##[derive(::std::fmt::Debug)] pub(crate) struct ServiceRuntimePlugin { - handle: std::sync::Arc, + config: #{Option}<#{FrozenLayer}>, + runtime_components: #{RuntimeComponentsBuilder}, } impl ServiceRuntimePlugin { - pub fn new(handle: std::sync::Arc) -> Self { Self { handle } } + pub fn new(_service_config: crate::config::Config) -> Self { + let config = { #{config} }; + let mut runtime_components = #{RuntimeComponentsBuilder}::new("ServiceRuntimePlugin"); + #{runtime_components} + Self { config, runtime_components } + } } impl #{RuntimePlugin} for ServiceRuntimePlugin { - fn configure(&self, cfg: &mut #{ConfigBag}) -> Result<(), #{BoxError}> { - use #{ConfigBagAccessors}; + fn config(&self) -> #{Option}<#{FrozenLayer}> { + self.config.clone() + } - cfg.set_identity_resolvers(#{IdentityResolvers}::builder().build()); - cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StubAuthOptionResolverParams}::new())); - cfg.set_auth_option_resolver(#{StubAuthOptionResolver}::new()); - cfg.set_endpoint_resolver(#{StaticUriEndpointResolver}::default()); - cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); - cfg.set_connection(#{TestConnection}::new(vec![])); - // TODO(RuntimePlugins): Add the HttpAuthSchemes to the config bag - // TODO(RuntimePlugins): Add the TraceProbe to the config bag - Ok(()) + fn runtime_components(&self) -> #{Cow}<'_, #{RuntimeComponentsBuilder}> { + #{Cow}::Borrowed(&self.runtime_components) } } + + /// Cross-operation shared-state singletons + #{declare_singletons} """, *codegenScope, + "config" to writable { + if (additionalConfig.isNotEmpty()) { + rustTemplate( + """ + let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); + #{additional_config} + #{Some}(cfg.freeze()) + """, + *codegenScope, + "additional_config" to additionalConfig, + ) + } else { + rust("None") + } + }, + "runtime_components" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterRuntimeComponents("_service_config")) + }, + "declare_singletons" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.DeclareSingletons()) + }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt new file mode 100644 index 0000000000..8859cc598f --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt @@ -0,0 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators.client + +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section + +sealed class CustomizableOperationSection(name: String) : Section(name) { + /** Write custom code into a customizable operation's impl block */ + data class CustomizableOperationImpl( + val isRuntimeModeOrchestrator: Boolean, + ) : CustomizableOperationSection("CustomizableOperationImpl") +} + +abstract class CustomizableOperationCustomization : NamedCustomization() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index ed24d439b7..5c2c4e63eb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -10,10 +10,15 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.GenericTypeArg import software.amazon.smithy.rust.codegen.core.rustlang.RustGenerics +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations /** * Generates the code required to add the `.customize()` function to the @@ -22,32 +27,30 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate class CustomizableOperationGenerator( private val codegenContext: ClientCodegenContext, private val generics: FluentClientGenerics, + private val customizations: List, ) { - private val includeFluentClient = codegenContext.settings.codegenConfig.includeFluentClient private val runtimeConfig = codegenContext.runtimeConfig private val smithyHttp = CargoDependency.smithyHttp(runtimeConfig).toType() private val smithyTypes = CargoDependency.smithyTypes(runtimeConfig).toType() fun render(crate: RustCrate) { crate.withModule(ClientRustModule.Client.customize) { - rustTemplate( - """ - pub use #{Operation}; - pub use #{Request}; - pub use #{Response}; - pub use #{ClassifyRetry}; - pub use #{RetryKind}; - """, - "Operation" to smithyHttp.resolve("operation::Operation"), - "Request" to smithyHttp.resolve("operation::Request"), - "Response" to smithyHttp.resolve("operation::Response"), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "RetryKind" to smithyTypes.resolve("retry::RetryKind"), - ) - renderCustomizableOperationModule(this) - - if (includeFluentClient) { - renderCustomizableOperationSend(this) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + pub use #{Operation}; + pub use #{Request}; + pub use #{Response}; + pub use #{ClassifyRetry}; + pub use #{RetryKind}; + """, + "Operation" to smithyHttp.resolve("operation::Operation"), + "Request" to smithyHttp.resolve("operation::Request"), + "Response" to smithyHttp.resolve("operation::Response"), + "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), + "RetryKind" to smithyTypes.resolve("retry::RetryKind"), + ) + renderCustomizableOperationModule(this) } } } @@ -58,33 +61,27 @@ class CustomizableOperationGenerator( val combinedGenerics = operationGenerics + handleGenerics val codegenScope = arrayOf( + *preludeScope, + "Arc" to RuntimeType.Arc, + "Infallible" to RuntimeType.stdConvert.resolve("Infallible"), // SDK Types - "http_result" to smithyHttp.resolve("result"), - "http_body" to smithyHttp.resolve("body"), "HttpRequest" to RuntimeType.HttpRequest, "handle_generics_decl" to handleGenerics.declaration(), "handle_generics_bounds" to handleGenerics.bounds(), "operation_generics_decl" to operationGenerics.declaration(), "combined_generics_decl" to combinedGenerics.declaration(), "customize_module" to ClientRustModule.Client.customize, + "SdkBody" to RuntimeType.sdkBody(runtimeConfig), ) writer.rustTemplate( """ - use crate::client::Handle; - - use #{http_body}::SdkBody; - use #{http_result}::SdkError; - - use std::convert::Infallible; - use std::sync::Arc; - /// A wrapper type for [`Operation`](aws_smithy_http::operation::Operation)s that allows for /// customization of the operation before it is sent. A `CustomizableOperation` may be sent /// by calling its [`.send()`][#{customize_module}::CustomizableOperation::send] method. ##[derive(Debug)] pub struct CustomizableOperation#{combined_generics_decl:W} { - pub(crate) handle: Arc, + pub(crate) handle: #{Arc}, pub(crate) operation: Operation#{operation_generics_decl:W}, } @@ -95,19 +92,19 @@ class CustomizableOperationGenerator( /// Allows for customizing the operation's request pub fn map_request( mut self, - f: impl FnOnce(#{HttpRequest}) -> Result<#{HttpRequest}, E>, - ) -> Result { + f: impl #{FnOnce}(#{HttpRequest}<#{SdkBody}>) -> #{Result}<#{HttpRequest}<#{SdkBody}>, E>, + ) -> #{Result} { let (request, response) = self.operation.into_request_response(); let request = request.augment(|req, _props| f(req))?; self.operation = Operation::from_parts(request, response); - Ok(self) + #{Ok}(self) } /// Convenience for `map_request` where infallible direct mutation of request is acceptable - pub fn mutate_request(self, f: impl FnOnce(&mut #{HttpRequest})) -> Self { + pub fn mutate_request(self, f: impl #{FnOnce}(&mut #{HttpRequest}<#{SdkBody}>)) -> Self { self.map_request(|mut req| { f(&mut req); - Result::<_, Infallible>::Ok(req) + #{Result}::<_, #{Infallible}>::Ok(req) }) .expect("infallible") } @@ -115,46 +112,269 @@ class CustomizableOperationGenerator( /// Allows for customizing the entire operation pub fn map_operation( mut self, - f: impl FnOnce(Operation#{operation_generics_decl:W}) -> Result, - ) -> Result { + f: impl #{FnOnce}(Operation#{operation_generics_decl:W}) -> #{Result}, + ) -> #{Result} { self.operation = f(self.operation)?; - Ok(self) + #{Ok}(self) } /// Direct access to read the HTTP request - pub fn request(&self) -> &#{HttpRequest} { + pub fn request(&self) -> &#{HttpRequest}<#{SdkBody}> { self.operation.request() } /// Direct access to mutate the HTTP request - pub fn request_mut(&mut self) -> &mut #{HttpRequest} { + pub fn request_mut(&mut self) -> &mut #{HttpRequest}<#{SdkBody}> { self.operation.request_mut() } + + #{additional_methods} } """, *codegenScope, + "additional_methods" to writable { + writeCustomizations(customizations, CustomizableOperationSection.CustomizableOperationImpl(false)) + }, ) } - private fun renderCustomizableOperationSend(writer: RustWriter) { - val smithyHttp = CargoDependency.smithyHttp(runtimeConfig).toType() - val smithyClient = CargoDependency.smithyClient(runtimeConfig).toType() - - val operationGenerics = RustGenerics(GenericTypeArg("O"), GenericTypeArg("Retry")) - val handleGenerics = generics.toRustGenerics() - val combinedGenerics = operationGenerics + handleGenerics - + fun renderForOrchestrator(crate: RustCrate) { val codegenScope = arrayOf( - "combined_generics_decl" to combinedGenerics.declaration(), - "handle_generics_bounds" to handleGenerics.bounds(), - "ParseHttpResponse" to smithyHttp.resolve("response::ParseHttpResponse"), - "NewRequestPolicy" to smithyClient.resolve("retry::NewRequestPolicy"), - "SmithyRetryPolicy" to smithyClient.resolve("bounds::SmithyRetryPolicy"), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + *preludeScope, + "CustomizableOperation" to ClientRustModule.Client.customize.toType() + .resolve("orchestrator::CustomizableOperation"), + "CustomizableSend" to ClientRustModule.Client.customize.toType() + .resolve("internal::CustomizableSend"), + "HttpRequest" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpRequest"), + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpResponse"), + "Interceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::interceptors::Interceptor"), + "MapRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig) + .resolve("client::interceptors::MapRequestInterceptor"), + "MutateRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig) + .resolve("client::interceptors::MutateRequestInterceptor"), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "SharedRuntimePlugin" to RuntimeType.sharedRuntimePlugin(runtimeConfig), + "SendResult" to ClientRustModule.Client.customize.toType() + .resolve("internal::SendResult"), + "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "SdkError" to RuntimeType.sdkError(runtimeConfig), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::interceptors::SharedInterceptor"), ) + val customizeModule = ClientRustModule.Client.customize + + crate.withModule(customizeModule) { + renderConvenienceAliases(customizeModule, this) + + // TODO(enableNewSmithyRuntimeCleanup): Render it directly under the customize module when CustomizableOperation + // in the middleware has been removed. + withInlineModule( + RustModule.new( + "orchestrator", + Visibility.PUBLIC, + true, + customizeModule, + documentationOverride = "Module for defining types for `CustomizableOperation` in the orchestrator", + ), + null, + ) { + rustTemplate( + """ + /// `CustomizableOperation` allows for configuring a single operation invocation before it is sent. + pub struct CustomizableOperation { + pub(crate) customizable_send: #{Box}>, + pub(crate) config_override: #{Option}, + pub(crate) interceptors: Vec<#{SharedInterceptor}>, + pub(crate) runtime_plugins: Vec<#{SharedRuntimePlugin}>, + } + + impl CustomizableOperation { + /// Adds an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline. + /// + /// Note that interceptors can also be added to `CustomizableOperation` by `config_override`, + /// `map_request`, and `mutate_request` (the last two are implemented via interceptors under the hood). + /// The order in which those user-specified operation interceptors are invoked should not be relied upon + /// as it is an implementation detail. + pub fn interceptor(mut self, interceptor: impl #{Interceptor} + 'static) -> Self { + self.interceptors.push(#{SharedInterceptor}::new(interceptor)); + self + } + + /// Adds a runtime plugin. + ##[allow(unused)] + pub(crate) fn runtime_plugin(mut self, runtime_plugin: impl #{RuntimePlugin} + 'static) -> Self { + self.runtime_plugins.push(#{SharedRuntimePlugin}::new(runtime_plugin)); + self + } + + /// Allows for customizing the operation's request. + pub fn map_request(mut self, f: F) -> Self + where + F: #{Fn}(#{HttpRequest}) -> #{Result}<#{HttpRequest}, MapE> + + #{Send} + + #{Sync} + + 'static, + MapE: ::std::error::Error + #{Send} + #{Sync} + 'static, + { + self.interceptors.push( + #{SharedInterceptor}::new( + #{MapRequestInterceptor}::new(f), + ), + ); + self + } + + /// Convenience for `map_request` where infallible direct mutation of request is acceptable. + pub fn mutate_request(mut self, f: F) -> Self + where + F: #{Fn}(&mut http::Request<#{SdkBody}>) + #{Send} + #{Sync} + 'static, + { + self.interceptors.push( + #{SharedInterceptor}::new( + #{MutateRequestInterceptor}::new(f), + ), + ); + self + } + + /// Overrides config for a single operation invocation. + /// + /// `config_override` is applied to the operation configuration level. + /// The fields in the builder that are `Some` override those applied to the service + /// configuration level. For instance, + /// + /// Config A overridden by Config B == Config C + /// field_1: None, field_1: Some(v2), field_1: Some(v2), + /// field_2: Some(v1), field_2: Some(v2), field_2: Some(v2), + /// field_3: Some(v1), field_3: None, field_3: Some(v1), + pub fn config_override( + mut self, + config_override: impl #{Into}, + ) -> Self { + self.config_override = Some(config_override.into()); + self + } + + /// Sends the request and returns the response. + pub async fn send( + self, + ) -> #{SendResult} + where + E: std::error::Error + #{Send} + #{Sync} + 'static, + { + let mut config_override = if let Some(config_override) = self.config_override { + config_override + } else { + crate::config::Builder::new() + }; + + self.interceptors.into_iter().for_each(|interceptor| { + config_override.push_interceptor(interceptor); + }); + self.runtime_plugins.into_iter().for_each(|plugin| { + config_override.push_runtime_plugin(plugin); + }); + + (self.customizable_send)(config_override).await + } + + #{additional_methods} + } + """, + *codegenScope, + "additional_methods" to writable { + writeCustomizations( + customizations, + CustomizableOperationSection.CustomizableOperationImpl(true), + ) + }, + ) + } + } + } + + private fun renderConvenienceAliases(parentModule: RustModule, writer: RustWriter) { + writer.withInlineModule(RustModule.new("internal", Visibility.PUBCRATE, true, parentModule), null) { + rustTemplate( + """ + pub type BoxFuture = ::std::pin::Pin<#{Box} + #{Send}>>; + + pub type SendResult = #{Result}< + T, + #{SdkError}< + E, + #{HttpResponse}, + >, + >; + + pub trait CustomizableSend: + #{FnOnce}(crate::config::Builder) -> BoxFuture> + {} + + impl CustomizableSend for F + where + F: #{FnOnce}(crate::config::Builder) -> BoxFuture> + {} + """, + *preludeScope, + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpResponse"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + ) + } + } +} + +fun renderCustomizableOperationSend(codegenContext: ClientCodegenContext, generics: FluentClientGenerics, writer: RustWriter) { + val runtimeConfig = codegenContext.runtimeConfig + val smithyHttp = CargoDependency.smithyHttp(runtimeConfig).toType() + val smithyClient = CargoDependency.smithyClient(runtimeConfig).toType() + + val operationGenerics = RustGenerics(GenericTypeArg("O"), GenericTypeArg("Retry")) + val handleGenerics = generics.toRustGenerics() + val combinedGenerics = operationGenerics + handleGenerics + + val codegenScope = arrayOf( + *preludeScope, + "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + // TODO(enableNewSmithyRuntimeCleanup): Delete the trait bounds when cleaning up middleware + "ParseHttpResponse" to smithyHttp.resolve("response::ParseHttpResponse"), + "NewRequestPolicy" to smithyClient.resolve("retry::NewRequestPolicy"), + "SmithyRetryPolicy" to smithyClient.resolve("bounds::SmithyRetryPolicy"), + "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), + // TODO(enableNewSmithyRuntimeCleanup): Delete the generics when cleaning up middleware + "combined_generics_decl" to combinedGenerics.declaration(), + "handle_generics_bounds" to handleGenerics.bounds(), + ) + + if (generics is FlexibleClientGenerics) { + writer.rustTemplate( + """ + impl#{combined_generics_decl:W} CustomizableOperation#{combined_generics_decl:W} + where + #{handle_generics_bounds:W} + { + /// Sends this operation's request + pub async fn send(self) -> #{Result}> + where + E: std::error::Error + #{Send} + #{Sync} + 'static, + O: #{ParseHttpResponse}> + #{Send} + #{Sync} + #{Clone} + 'static, + Retry: #{Send} + #{Sync} + #{Clone}, + Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + #{Send} + #{Sync} + #{Clone}, + ::Policy: #{SmithyRetryPolicy} + #{Clone}, + { + self.handle.client.call(self.operation).await + } + } + """, + *codegenScope, + ) + } else if (codegenContext.smithyRuntimeMode.generateMiddleware) { writer.rustTemplate( """ impl#{combined_generics_decl:W} CustomizableOperation#{combined_generics_decl:W} @@ -162,13 +382,12 @@ class CustomizableOperationGenerator( #{handle_generics_bounds:W} { /// Sends this operation's request - pub async fn send(self) -> Result> + pub async fn send(self) -> #{Result}> where - E: std::error::Error + Send + Sync + 'static, - O: #{ParseHttpResponse}> + Send + Sync + Clone + 'static, - Retry: Send + Sync + Clone, - Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + Send + Sync + Clone, - ::Policy: #{SmithyRetryPolicy} + Clone, + E: std::error::Error + #{Send} + #{Sync} + 'static, + O: #{ParseHttpResponse}> + #{Send} + #{Sync} + #{Clone} + 'static, + Retry: #{Send} + #{Sync} + #{Clone}, + Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + #{Send} + #{Sync} + #{Clone}, { self.handle.client.call(self.operation).await } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientCore.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientCore.kt index fb9750119b..3073413efe 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientCore.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientCore.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName class FluentClientCore(private val model: Model) { @@ -67,4 +68,15 @@ class FluentClientCore(private val model: Model) { write("self") } } + + /** + * Generate and write Rust code for a getter method that returns a reference to the inner data. + */ + fun RustWriter.renderGetterHelper(member: MemberShape, memberName: String, coreType: RustType) { + documentShape(member, model) + deprecatedShape(member) + withBlockTemplate("pub fn $memberName(&self) -> &#{CoreType} {", "}", "CoreType" to coreType) { + write("self.inner.$memberName()") + } + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt index bc959d4a74..9775a342fb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt @@ -9,12 +9,14 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section @@ -34,12 +36,27 @@ class FluentClientDecorator : ClientCodegenDecorator { return } + val generics = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + NoClientGenerics(codegenContext.runtimeConfig) + } else { + FlexibleClientGenerics( + connectorDefault = null, + middlewareDefault = null, + retryDefault = RuntimeType.smithyClient(codegenContext.runtimeConfig).resolve("retry::Standard"), + client = RuntimeType.smithyClient(codegenContext.runtimeConfig), + ) + } + FluentClientGenerator( codegenContext, + generics = generics, customizations = listOf(GenericFluentClient(codegenContext)), ).render(rustCrate) + rustCrate.withModule(ClientRustModule.Client.customize) { + renderCustomizableOperationSend(codegenContext, generics, this) + } + rustCrate.mergeFeature(Feature("rustls", default = true, listOf("aws-smithy-client/rustls"))) - rustCrate.mergeFeature(Feature("native-tls", default = false, listOf("aws-smithy-client/native-tls"))) } override fun libRsCustomizations( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt index 3e8c66f64b..b83cb7a860 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt @@ -202,13 +202,13 @@ object FluentClientDocs { A client has a function for every operation that can be performed by the service. For example, the [`${operationSymbol.name}`](${operationSymbol.namespace}) operation has a [`Client::$operationFnName`], function which returns a builder for that operation. - The fluent builder ultimately has a `call()` function that returns an async future that + The fluent builder ultimately has a `send()` function that returns an async future that returns a result, as illustrated below: ```rust,ignore let result = client.$operationFnName() .${memberSymbol.name}("example") - .call() + .send() .await; ``` diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 6669ee04f1..d7fb4560b5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -30,19 +30,25 @@ import software.amazon.smithy.rust.codegen.core.rustlang.docLink import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.escape +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.rustlang.normalizeHtml import software.amazon.smithy.rust.codegen.core.rustlang.qualifiedName import software.amazon.smithy.rust.codegen.core.rustlang.render +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTypeParameters import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter +import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.smithy.generators.getterName import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -53,18 +59,15 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase class FluentClientGenerator( private val codegenContext: ClientCodegenContext, private val reexportSmithyClientBuilder: Boolean = true, - private val generics: FluentClientGenerics = FlexibleClientGenerics( - connectorDefault = null, - middlewareDefault = null, - retryDefault = RuntimeType.smithyClient(codegenContext.runtimeConfig).resolve("retry::Standard"), - client = RuntimeType.smithyClient(codegenContext.runtimeConfig), - ), + private val generics: FluentClientGenerics, private val customizations: List = emptyList(), - private val retryClassifier: RuntimeType = RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("retry::DefaultResponseRetryClassifier"), + private val retryClassifier: RuntimeType = RuntimeType.smithyHttp(codegenContext.runtimeConfig) + .resolve("retry::DefaultResponseRetryClassifier"), ) { companion object { fun clientOperationFnName(operationShape: OperationShape, symbolProvider: RustSymbolProvider): String = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operationShape).name.toSnakeCase()) + fun clientOperationModuleName(operationShape: OperationShape, symbolProvider: RustSymbolProvider): String = RustReservedWords.escapeIfNeeded( symbolProvider.toSymbol(operationShape).name.toSnakeCase(), @@ -79,17 +82,22 @@ class FluentClientGenerator( private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val core = FluentClientCore(model) + private val smithyRuntimeMode = codegenContext.smithyRuntimeMode - fun render(crate: RustCrate) { + fun render(crate: RustCrate, customizableOperationCustomizations: List = emptyList()) { renderFluentClient(crate) + val customizableOperationGenerator = CustomizableOperationGenerator(codegenContext, generics, customizableOperationCustomizations) operations.forEach { operation -> crate.withModule(symbolProvider.moduleForBuilder(operation)) { renderFluentBuilder(operation) } } - CustomizableOperationGenerator(codegenContext, generics).render(crate) + customizableOperationGenerator.render(crate) + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + customizableOperationGenerator.renderForOrchestrator(crate) + } } private fun renderFluentClient(crate: RustCrate) { @@ -103,51 +111,9 @@ class FluentClientGenerator( "client" to RuntimeType.smithyClient(runtimeConfig), ) } - rustTemplate( - """ - ##[derive(Debug)] - pub(crate) struct Handle#{generics_decl:W} { - pub(crate) client: #{client}::Client#{smithy_inst:W}, - pub(crate) conf: crate::Config, - } - - #{client_docs:W} - ##[derive(std::fmt::Debug)] - pub struct Client#{generics_decl:W} { - handle: std::sync::Arc - } - - impl${generics.inst} std::clone::Clone for Client${generics.inst} { - fn clone(&self) -> Self { - Self { handle: self.handle.clone() } - } - } - - impl${generics.inst} From<#{client}::Client#{smithy_inst:W}> for Client${generics.inst} { - fn from(client: #{client}::Client#{smithy_inst:W}) -> Self { - Self::with_config(client, crate::Config::builder().build()) - } - } - - impl${generics.inst} Client${generics.inst} { - /// Creates a client with the given service configuration. - pub fn with_config(client: #{client}::Client#{smithy_inst:W}, conf: crate::Config) -> Self { - Self { - handle: std::sync::Arc::new(Handle { - client, - conf, - }) - } - } - - /// Returns the client's configuration. - pub fn conf(&self) -> &crate::Config { - &self.handle.conf - } - } - """, - "generics_decl" to generics.decl, - "smithy_inst" to generics.smithyInst, + val clientScope = arrayOf( + *preludeScope, + "Arc" to RuntimeType.Arc, "client" to RuntimeType.smithyClient(runtimeConfig), "client_docs" to writable { @@ -159,7 +125,128 @@ class FluentClientGenerator( )(this) } }, + "RetryConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryConfig"), + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), + "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), + // TODO(enableNewSmithyRuntimeCleanup): Delete the generics when cleaning up middleware + "generics_decl" to generics.decl, + "smithy_inst" to generics.smithyInst, ) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + ##[derive(Debug)] + pub(crate) struct Handle#{generics_decl:W} { + pub(crate) client: #{client}::Client#{smithy_inst:W}, + pub(crate) conf: crate::Config, + } + + #{client_docs:W} + ##[derive(::std::fmt::Debug)] + pub struct Client#{generics_decl:W} { + handle: #{Arc} + } + + impl${generics.inst} #{Clone} for Client${generics.inst} { + fn clone(&self) -> Self { + Self { handle: self.handle.clone() } + } + } + + impl${generics.inst} From<#{client}::Client#{smithy_inst:W}> for Client${generics.inst} { + fn from(client: #{client}::Client#{smithy_inst:W}) -> Self { + Self::with_config(client, crate::Config::builder().build()) + } + } + + impl${generics.inst} Client${generics.inst} { + /// Creates a client with the given service configuration. + pub fn with_config(client: #{client}::Client#{smithy_inst:W}, conf: crate::Config) -> Self { + Self { + handle: #{Arc}::new(Handle { + client, + conf, + }) + } + } + + /// Returns the client's configuration. + pub fn conf(&self) -> &crate::Config { + &self.handle.conf + } + } + """, + *clientScope, + ) + } else { + rustTemplate( + """ + ##[derive(Debug)] + pub(crate) struct Handle { + pub(crate) conf: crate::Config, + pub(crate) runtime_plugins: #{RuntimePlugins}, + } + + #{client_docs:W} + ##[derive(#{Clone}, ::std::fmt::Debug)] + pub struct Client { + handle: #{Arc}, + } + + impl Client { + /// Creates a new client from the service [`Config`](crate::Config). + /// + /// ## Panics + /// + /// - This method will panic if the `conf` is missing an async sleep implementation. If you experience this panic, set + /// the `sleep_impl` on the Config passed into this function to fix it. + /// - This method will panic if the `conf` is missing an HTTP connector. If you experience this panic, set the + /// `http_connector` on the Config passed into this function to fix it. + pub fn from_conf(conf: crate::Config) -> Self { + let retry_config = conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); + let timeout_config = conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); + let sleep_impl = conf.sleep_impl(); + if (retry_config.has_retry() || timeout_config.has_timeouts()) && sleep_impl.is_none() { + panic!("An async sleep implementation is required for retries or timeouts to work. \ + Set the `sleep_impl` on the Config passed into this function to fix this panic."); + } + + Self { + handle: #{Arc}::new( + Handle { + conf: conf.clone(), + runtime_plugins: #{base_client_runtime_plugins}(conf), + } + ) + } + } + + /// Returns the client's configuration. + pub fn config(&self) -> &crate::Config { + &self.handle.conf + } + + ##[doc(hidden)] + // TODO(enableNewSmithyRuntimeCleanup): Delete this function when cleaning up middleware + // This is currently kept around so the tests still compile in both modes + /// Creates a client with the given service configuration. + pub fn with_config(_client: #{client}::Client, conf: crate::Config) -> Self { + Self::from_conf(conf) + } + + ##[doc(hidden)] + // TODO(enableNewSmithyRuntimeCleanup): Delete this function when cleaning up middleware + // This is currently kept around so the tests still compile in both modes + /// Returns the client's configuration. + pub fn conf(&self) -> &crate::Config { + &self.handle.conf + } + } + """, + *clientScope, + "base_client_runtime_plugins" to baseClientRuntimePluginsFn(runtimeConfig), + ) + } } operations.forEach { operation -> @@ -231,10 +318,50 @@ class FluentClientGenerator( } private fun RustWriter.renderFluentBuilder(operation: OperationShape) { + val outputType = symbolProvider.toSymbol(operation.outputShape(model)) + val errorType = symbolProvider.symbolForOperationError(operation) val operationSymbol = symbolProvider.toSymbol(operation) + val input = operation.inputShape(model) val baseDerives = symbolProvider.toSymbol(input).expectRustMetadata().derives // Filter out any derive that isn't Clone. Then add a Debug derive + // input name + val fnName = clientOperationFnName(operation, symbolProvider) + implBlock(symbolProvider.symbolForBuilder(input)) { + rustTemplate( + """ + /// Sends a request with this input using the given client. + pub async fn send_with${generics.inst}( + self, + client: &crate::Client${generics.inst} + ) -> #{Result}< + #{OperationOutput}, + #{SdkError}< + #{OperationError}, + #{RawResponseType} + > + > #{send_bounds:W} #{boundsWithoutWhereClause:W} { + let mut fluent_builder = client.$fnName(); + fluent_builder.inner = self; + fluent_builder.send().await + } + """, + *preludeScope, + "RawResponseType" to if (codegenContext.smithyRuntimeMode.generateMiddleware) { + RuntimeType.smithyHttp(runtimeConfig).resolve("operation::Response") + } else { + RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse") + }, + "Operation" to operationSymbol, + "OperationError" to errorType, + "OperationOutput" to outputType, + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + "boundsWithoutWhereClause" to generics.boundsWithoutWhereClause, + "send_bounds" to generics.sendBounds(operationSymbol, outputType, errorType, retryClassifier), + ) + } + val derives = baseDerives.filter { it == RuntimeType.Clone } + RuntimeType.Debug docs("Fluent builder constructing a request to `${operationSymbol.name}`.\n") @@ -242,133 +369,215 @@ class FluentClientGenerator( documentShape(operation, model, autoSuppressMissingDocs = false) deprecatedShape(operation) Attribute(derive(derives.toSet())).render(this) - rustTemplate( - """ - pub struct $builderName#{generics:W} { - handle: std::sync::Arc, - inner: #{Inner} - } - """, - "Inner" to symbolProvider.symbolForBuilder(input), - "client" to RuntimeType.smithyClient(runtimeConfig), + withBlockTemplate( + "pub struct $builderName#{generics:W} {", + "}", "generics" to generics.decl, - "operation" to operationSymbol, - ) + ) { + rustTemplate( + """ + handle: #{Arc}, + inner: #{Inner}, + """, + "Inner" to symbolProvider.symbolForBuilder(input), + "Arc" to RuntimeType.Arc, + "generics" to generics.decl, + ) + if (smithyRuntimeMode.generateOrchestrator) { + rustTemplate("config_override: #{Option},", *preludeScope) + } + } rustBlockTemplate( "impl${generics.inst} $builderName${generics.inst} #{bounds:W}", "client" to RuntimeType.smithyClient(runtimeConfig), "bounds" to generics.bounds, ) { - val outputType = symbolProvider.toSymbol(operation.outputShape(model)) - val errorType = symbolProvider.symbolForOperationError(operation) - - // Have to use fully-qualified result here or else it could conflict with an op named Result - rustTemplate( - """ - /// Creates a new `${operationSymbol.name}`. - pub(crate) fn new(handle: std::sync::Arc) -> Self { - Self { handle, inner: Default::default() } + rust("/// Creates a new `${operationSymbol.name}`.") + withBlockTemplate( + "pub(crate) fn new(handle: #{Arc}) -> Self {", + "}", + "Arc" to RuntimeType.Arc, + "generics" to generics.decl, + ) { + withBlockTemplate( + "Self {", + "}", + ) { + rustTemplate("handle, inner: #{Default}::default(),", *preludeScope) + if (smithyRuntimeMode.generateOrchestrator) { + rustTemplate("config_override: #{None},", *preludeScope) + } } + } - /// Consume this builder, creating a customizable operation that can be modified before being - /// sent. The operation's inner [http::Request] can be modified as well. - pub async fn customize(self) -> std::result::Result< - #{CustomizableOperation}#{customizable_op_type_params:W}, - #{SdkError}<#{OperationError}> - > #{send_bounds:W} { - let handle = self.handle.clone(); - let operation = self.inner.build().map_err(#{SdkError}::construction_failure)? - .make_operation(&handle.conf) - .await - .map_err(#{SdkError}::construction_failure)?; - Ok(#{CustomizableOperation} { handle, operation }) - } + rust("/// Access the ${operationSymbol.name} as a reference.\n") + withBlockTemplate( + "pub fn as_input(&self) -> &#{Inner} {", "}", + "Inner" to symbolProvider.symbolForBuilder(input), + ) { + write("&self.inner") + } - /// Sends the request and returns the response. - /// - /// If an error occurs, an `SdkError` will be returned with additional details that - /// can be matched against. - /// - /// By default, any retryable failures will be retried twice. Retry behavior - /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be - /// set when configuring the client. - pub async fn send(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}>> - #{send_bounds:W} { - let op = self.inner.build().map_err(#{SdkError}::construction_failure)? - .make_operation(&self.handle.conf) - .await - .map_err(#{SdkError}::construction_failure)?; - self.handle.client.call(op).await - } - """, - "CustomizableOperation" to ClientRustModule.Client.customize.toType() - .resolve("CustomizableOperation"), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "OperationError" to errorType, - "OperationOutput" to outputType, - "SdkError" to RuntimeType.sdkError(runtimeConfig), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), - "send_bounds" to generics.sendBounds(operationSymbol, outputType, errorType, retryClassifier), - "customizable_op_type_params" to rustTypeParameters( - symbolProvider.toSymbol(operation), - retryClassifier, - generics.toRustGenerics(), - ), - ) - if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { - rustTemplate( - """ - // TODO(enableNewSmithyRuntime): Replace `send` with `send_v2` - /// Sends the request and returns the response. - /// - /// If an error occurs, an `SdkError` will be returned with additional details that - /// can be matched against. - /// - /// By default, any retryable failures will be retried twice. Retry behavior - /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be - /// set when configuring the client. - pub async fn send_v2(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - let runtime_plugins = #{RuntimePlugins}::new() - .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())) - .with_operation_plugin(#{Operation}::new()); - let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; - let input = #{TypedBox}::new(input).erase(); - let output = #{invoke}(input, &runtime_plugins) - .await - .map_err(|err| { - err.map_service_error(|err| { - #{TypedBox}::<#{OperationError}>::assume_from(err) - .expect("correct error type") - .unwrap() - }) - })?; - Ok(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) - } - """, - "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse"), + if (smithyRuntimeMode.generateMiddleware) { + val middlewareScope = arrayOf( + *preludeScope, + "CustomizableOperation" to ClientRustModule.Client.customize.toType() + .resolve("CustomizableOperation"), + "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), + "Operation" to operationSymbol, "OperationError" to errorType, - "Operation" to symbolProvider.toSymbol(operation), "OperationOutput" to outputType, - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugins"), "SdkError" to RuntimeType.sdkError(runtimeConfig), - "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), - "invoke" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke"), + "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + "send_bounds" to generics.sendBounds(operationSymbol, outputType, errorType, retryClassifier), + "customizable_op_type_params" to rustTypeParameters( + symbolProvider.toSymbol(operation), + retryClassifier, + generics.toRustGenerics(), + ), ) + if (smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + /// Sends the request and returns the response. + /// + /// If an error occurs, an `SdkError` will be returned with additional details that + /// can be matched against. + /// + /// By default, any retryable failures will be retried twice. Retry behavior + /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be + /// set when configuring the client. + pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> + #{send_bounds:W} { + let op = self.inner.build().map_err(#{SdkError}::construction_failure)? + .make_operation(&self.handle.conf) + .await + .map_err(#{SdkError}::construction_failure)?; + self.handle.client.call(op).await + } + + /// Consumes this builder, creating a customizable operation that can be modified before being + /// sent. The operation's inner [http::Request] can be modified as well. + pub async fn customize(self) -> #{Result}< + #{CustomizableOperation}#{customizable_op_type_params:W}, + #{SdkError}<#{OperationError}> + > #{send_bounds:W} { + let handle = self.handle.clone(); + let operation = self.inner.build().map_err(#{SdkError}::construction_failure)? + .make_operation(&handle.conf) + .await + .map_err(#{SdkError}::construction_failure)?; + #{Ok}(#{CustomizableOperation} { handle, operation }) + } + """, + *middlewareScope, + ) + } } - PaginatorGenerator.paginatorType(codegenContext, generics, operation, retryClassifier)?.also { paginatorType -> + + if (smithyRuntimeMode.generateOrchestrator) { + val orchestratorScope = arrayOf( + *preludeScope, + "CustomizableOperation" to ClientRustModule.Client.customize.toType() + .resolve("orchestrator::CustomizableOperation"), + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpResponse"), + "Operation" to operationSymbol, + "OperationError" to errorType, + "OperationOutput" to outputType, + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), + "SendResult" to ClientRustModule.Client.customize.toType() + .resolve("internal::SendResult"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + ) + if (smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Sends the request and returns the response. + /// + /// If an error occurs, an `SdkError` will be returned with additional details that + /// can be matched against. + /// + /// By default, any retryable failures will be retried twice. Retry behavior + /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be + /// set when configuring the client. + pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; + let runtime_plugins = #{Operation}::operation_runtime_plugins( + self.handle.runtime_plugins.clone(), + &self.handle.conf, + self.config_override, + ); + #{Operation}::orchestrate(&runtime_plugins, input).await + } + + /// Consumes this builder, creating a customizable operation that can be modified before being + /// sent. + // TODO(enableNewSmithyRuntimeCleanup): Remove `async` and `Result` once we switch to orchestrator + pub async fn customize( + self, + ) -> #{Result}< + #{CustomizableOperation}< + #{OperationOutput}, + #{OperationError}, + >, + #{SdkError}<#{OperationError}>, + > + { + #{Ok}(#{CustomizableOperation} { + customizable_send: #{Box}::new(move |config_override| { + #{Box}::pin(async { + self.config_override(config_override) + .send() + .await + }) + }), + config_override: None, + interceptors: vec![], + runtime_plugins: vec![], + }) + } + """, + *orchestratorScope, + ) + } + rustTemplate( """ - /// Create a paginator for this request - /// - /// Paginators are used by calling [`send().await`](#{Paginator}::send) which returns a `Stream`. - pub fn into_paginator(self) -> #{Paginator}${generics.inst} { - #{Paginator}::new(self.handle, self.inner) + pub(crate) fn config_override( + mut self, + config_override: impl Into, + ) -> Self { + self.set_config_override(Some(config_override.into())); + self + } + + pub(crate) fn set_config_override( + &mut self, + config_override: Option, + ) -> &mut Self { + self.config_override = config_override; + self } """, - "Paginator" to paginatorType, ) } + + PaginatorGenerator.paginatorType(codegenContext, generics, operation, retryClassifier) + ?.also { paginatorType -> + rustTemplate( + """ + /// Create a paginator for this request + /// + /// Paginators are used by calling [`send().await`](#{Paginator}::send) which returns a `Stream`. + pub fn into_paginator(self) -> #{Paginator}${generics.inst} { + #{Paginator}::new(self.handle, self.inner) + } + """, + "Paginator" to paginatorType, + ) + } writeCustomizations( customizations, FluentClientSection.FluentBuilderImpl( @@ -390,11 +599,46 @@ class FluentClientGenerator( val setterName = member.setterName() val optionalInputType = outerType.asOptional() with(core) { renderInputHelper(member, setterName, optionalInputType) } + + val getterName = member.getterName() + with(core) { renderGetterHelper(member, getterName, optionalInputType) } } } } } +private fun baseClientRuntimePluginsFn(runtimeConfig: RuntimeConfig): RuntimeType = + RuntimeType.forInlineFun("base_client_runtime_plugins", ClientRustModule.config) { + rustTemplate( + """ + pub(crate) fn base_client_runtime_plugins( + mut config: crate::Config, + ) -> #{RuntimePlugins} { + let mut configured_plugins = #{Vec}::new(); + ::std::mem::swap(&mut config.runtime_plugins, &mut configured_plugins); + let mut plugins = #{RuntimePlugins}::new() + .with_client_plugin( + #{StaticRuntimePlugin}::new() + .with_config(config.config.clone()) + .with_runtime_components(config.runtime_components.clone()) + ) + .with_client_plugin(crate::config::ServiceRuntimePlugin::new(config)) + .with_client_plugin(#{NoAuthRuntimePlugin}::new()); + for plugin in configured_plugins { + plugins = plugins.with_client_plugin(plugin); + } + plugins + } + """, + *preludeScope, + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), + "NoAuthRuntimePlugin" to RuntimeType.smithyRuntime(runtimeConfig) + .resolve("client::auth::no_auth::NoAuthRuntimePlugin"), + "StaticRuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::StaticRuntimePlugin"), + ) + } + /** * For a given `operation` shape, return a list of strings where each string describes the name and input type of one of * the operation's corresponding fluent builder methods as well as that method's documentation from the smithy model diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt index 399085d5e5..77b4a46019 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt @@ -12,8 +12,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +// TODO(enableNewSmithyRuntimeCleanup): Delete this client generics on/off switch headache interface FluentClientGenerics { /** Declaration with defaults set */ val decl: Writable @@ -32,6 +34,42 @@ interface FluentClientGenerics { /** Convert this `FluentClientGenerics` into the more general `RustGenerics` */ fun toRustGenerics(): RustGenerics + + /** bounds without where clause. If bounds does is not prefixed with `where\n`, then it gets the same value. **/ + val boundsWithoutWhereClause: Writable +} + +class NoClientGenerics(private val runtimeConfig: RuntimeConfig) : FluentClientGenerics { + /** Declaration with defaults set */ + override val decl = writable { } + + /** Instantiation of the Smithy client generics */ + override val smithyInst = writable { + rustTemplate( + "<#{DynConnector}, #{DynMiddleware}<#{DynConnector}>>", + "DynConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("erase::DynConnector"), + "DynMiddleware" to RuntimeType.smithyClient(runtimeConfig).resolve("erase::DynMiddleware"), + ) + } + + /** Instantiation */ + override val inst = "" + + /** Trait bounds */ + override val bounds = writable { } + + override val boundsWithoutWhereClause = writable {} + + /** Bounds for generated `send()` functions */ + override fun sendBounds( + operation: Symbol, + operationOutput: Symbol, + operationError: Symbol, + retryClassifier: RuntimeType, + ): Writable = + writable { } + + override fun toRustGenerics() = RustGenerics() } data class FlexibleClientGenerics( @@ -61,9 +99,18 @@ data class FlexibleClientGenerics( rustTemplate( """ where - C: #{client}::bounds::SmithyConnector, - M: #{client}::bounds::SmithyMiddleware, - R: #{client}::retry::NewRequestPolicy, + #{bounds} + """, + "bounds" to boundsWithoutWhereClause, + ) + } + + override val boundsWithoutWhereClause = writable { + rustTemplate( + """ + C: #{client}::bounds::SmithyConnector, + M: #{client}::bounds::SmithyMiddleware, + R: #{client}::retry::NewRequestPolicy, """, "client" to client, ) @@ -79,7 +126,7 @@ data class FlexibleClientGenerics( #{OperationOutput}, #{OperationError}, #{RetryClassifier} - > + >, """, "client" to client, "Operation" to operation, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/EventStreamSigningConfig.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/EventStreamSigningConfig.kt deleted file mode 100644 index 35da7d63b0..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/EventStreamSigningConfig.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.client.smithy.generators.config - -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType - -open class EventStreamSigningConfig( - runtimeConfig: RuntimeConfig, -) : ConfigCustomization() { - private val codegenScope = arrayOf( - "SharedPropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::SharedPropertyBag"), - "SignMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::SignMessage"), - ) - - override fun section(section: ServiceConfig): Writable { - return when (section) { - is ServiceConfig.ConfigImpl -> configImplSection() - else -> emptySection - } - } - - open fun configImplSection(): Writable = emptySection - - fun renderEventStreamSignerFn(signerInstantiator: (String) -> Writable): Writable = writable { - rustTemplate( - """ - /// Creates a new Event Stream `SignMessage` implementor. - pub fn new_event_stream_signer( - &self, - _properties: #{SharedPropertyBag} - ) -> impl #{SignMessage} { - #{signer:W} - } - """, - *codegenScope, - "signer" to signerInstantiator("_properties"), - ) - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt index 75b075dc4d..2606ff5f43 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt @@ -5,107 +5,129 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization /** - * Add a `make_token` field to Service config. See below for the resulting generated code. + * Add an `idempotency_token_provider` field to Service config. */ -class IdempotencyTokenProviderCustomization : NamedCustomization() { +class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext) : NamedCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode + private val codegenScope = arrayOf( + *preludeScope, + "default_provider" to RuntimeType.idempotencyToken(runtimeConfig).resolve("default_provider"), + "IdempotencyTokenProvider" to RuntimeType.idempotencyToken(runtimeConfig).resolve("IdempotencyTokenProvider"), + ) + override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { - rust("pub (crate) make_token: #T::IdempotencyTokenProvider,", RuntimeType.IdempotencyToken) + if (runtimeMode.generateMiddleware) { + rustTemplate("pub (crate) idempotency_token_provider: #{IdempotencyTokenProvider},", *codegenScope) + } } ServiceConfig.ConfigImpl -> writable { - rust( - """ - /// Returns a copy of the idempotency token provider. - /// If a random token provider was configured, - /// a newly-randomized token provider will be returned. - pub fn make_token(&self) -> #T::IdempotencyTokenProvider { - self.make_token.clone() - } - """, - RuntimeType.IdempotencyToken, - ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Returns a copy of the idempotency token provider. + /// If a random token provider was configured, + /// a newly-randomized token provider will be returned. + pub fn idempotency_token_provider(&self) -> #{IdempotencyTokenProvider} { + self.config.load::<#{IdempotencyTokenProvider}>().expect("the idempotency provider should be set").clone() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns a copy of the idempotency token provider. + /// If a random token provider was configured, + /// a newly-randomized token provider will be returned. + pub fn idempotency_token_provider(&self) -> #{IdempotencyTokenProvider} { + self.idempotency_token_provider.clone() + } + """, + *codegenScope, + ) + } } ServiceConfig.BuilderStruct -> writable { - rust("make_token: Option<#T::IdempotencyTokenProvider>,", RuntimeType.IdempotencyToken) + if (runtimeMode.generateMiddleware) { + rustTemplate("idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>,", *codegenScope) + } } ServiceConfig.BuilderImpl -> writable { rustTemplate( """ /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn make_token(mut self, make_token: impl Into<#{TokenProvider}>) -> Self { - self.set_make_token(Some(make_token.into())); - self - } - - /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn set_make_token(&mut self, make_token: Option<#{TokenProvider}>) -> &mut Self { - self.make_token = make_token; + pub fn idempotency_token_provider(mut self, idempotency_token_provider: impl #{Into}<#{IdempotencyTokenProvider}>) -> Self { + self.set_idempotency_token_provider(#{Some}(idempotency_token_provider.into())); self } """, - "TokenProvider" to RuntimeType.IdempotencyToken.resolve("IdempotencyTokenProvider"), + *codegenScope, ) + + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + /// Sets the idempotency token provider to use for service calls that require tokens. + pub fn set_idempotency_token_provider(&mut self, idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { + self.config.store_or_unset(idempotency_token_provider); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the idempotency token provider to use for service calls that require tokens. + pub fn set_idempotency_token_provider(&mut self, idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { + self.idempotency_token_provider = idempotency_token_provider; + self + } + """, + *codegenScope, + ) + } } ServiceConfig.BuilderBuild -> writable { - rust("make_token: self.make_token.unwrap_or_else(#T::default_provider),", RuntimeType.IdempotencyToken) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + if !resolver.is_set::<#{IdempotencyTokenProvider}>() { + resolver.config_mut().store_put(#{default_provider}()); + } + """, + *codegenScope, + ) + } else { + rustTemplate( + "idempotency_token_provider: self.idempotency_token_provider.unwrap_or_else(#{default_provider}),", + *codegenScope, + ) + } } is ServiceConfig.DefaultForTests -> writable { - rust("""${section.configBuilderRef}.set_make_token(Some("00000000-0000-4000-8000-000000000000".into()));""") + rust("""${section.configBuilderRef}.set_idempotency_token_provider(Some("00000000-0000-4000-8000-000000000000".into()));""") } else -> writable { } } } } - -/* Generated Code -pub struct Config { - pub(crate) make_token: Box, -} -impl Config { - pub fn builder() -> Builder { - Builder::default() - } -} -#[derive(Default)] -pub struct Builder { - #[allow(dead_code)] - make_token: Option>, -} -impl Builder { - pub fn new() -> Self { - Self::default() - } - - /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn make_token( - mut self, - make_token: impl crate::idempotency_token::MakeIdempotencyToken + 'static, - ) -> Self { - self.make_token = Some(Box::new(make_token)); - self - } - - pub fn build(self) -> Config { - Config { - make_token: self - .make_token - .unwrap_or_else(|| Box::new(crate::idempotency_token::default_provider())), - } - } -} - */ diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 00bc592ff4..bfa244e8d4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -11,22 +11,27 @@ import software.amazon.smithy.model.knowledge.OperationIndex import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.docsOrFallback -import software.amazon.smithy.rust.codegen.core.rustlang.raw import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf /** * [ServiceConfig] is the parent type of sections that can be overridden when generating a config for a service. @@ -85,6 +90,16 @@ sealed class ServiceConfig(name: String) : Section(name) { */ object BuilderBuild : ServiceConfig("BuilderBuild") + /** + * A section for customizing individual fields in the initializer of Config + */ + object BuilderBuildExtras : ServiceConfig("BuilderBuildExtras") + + /** + * A section for setting up a field to be used by ConfigOverrideRuntimePlugin + */ + data class OperationConfigOverride(val cfg: String) : ServiceConfig("ToRuntimePlugin") + /** * A section for extra functionality that needs to be defined with the config module */ @@ -96,7 +111,78 @@ sealed class ServiceConfig(name: String) : Section(name) { data class DefaultForTests(val configBuilderRef: String) : ServiceConfig("DefaultForTests") } -data class ConfigParam(val name: String, val type: Symbol, val setterDocs: Writable?, val getterDocs: Writable? = null) +data class ConfigParam( + val name: String, + val type: Symbol, + val newtype: RuntimeType?, + val setterDocs: Writable?, + val getterDocs: Writable? = null, + val optional: Boolean = true, +) { + + data class Builder( + var name: String? = null, + var type: Symbol? = null, + var newtype: RuntimeType? = null, + var setterDocs: Writable? = null, + var getterDocs: Writable? = null, + var optional: Boolean = true, + ) { + fun name(name: String) = apply { this.name = name } + fun type(type: Symbol) = apply { this.type = type } + fun newtype(newtype: RuntimeType) = apply { this.newtype = newtype } + fun setterDocs(setterDocs: Writable?) = apply { this.setterDocs = setterDocs } + fun getterDocs(getterDocs: Writable?) = apply { this.getterDocs = getterDocs } + fun optional(optional: Boolean) = apply { this.optional = optional } + fun build() = ConfigParam(name!!, type!!, newtype, setterDocs, getterDocs, optional) + } +} + +/** + * Generate a [RuntimeType] for a newtype whose name is [newtypeName] that wraps [inner]. + * + * When config parameters are stored in a config map in Rust, stored parameters are keyed by type. + * Therefore, primitive types, such as bool and String, need to be wrapped in newtypes to make them distinct. + */ +fun configParamNewtype(newtypeName: String, inner: Symbol, runtimeConfig: RuntimeConfig) = + RuntimeType.forInlineFun(newtypeName, ClientRustModule.config) { + val codegenScope = arrayOf( + "Storable" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Storable"), + "StoreReplace" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::StoreReplace"), + ) + rustTemplate( + """ + ##[derive(Debug, Clone)] + pub(crate) struct $newtypeName(pub(crate) $inner); + impl #{Storable} for $newtypeName { + type Storer = #{StoreReplace}; + } + """, + *codegenScope, + ) + } + +/** + * Render an expression that loads a value from a config bag. + * + * The expression to be rendered handles a case where a newtype is stored in the config bag, but the user expects + * the underlying raw type after the newtype has been loaded from the bag. + */ +fun loadFromConfigBag(innerTypeName: String, newtype: RuntimeType): Writable = writable { + rustTemplate( + """ + load::<#{newtype}>().map(#{f}) + """, + "newtype" to newtype, + "f" to writable { + if (innerTypeName == "bool") { + rust("|ty| ty.0") + } else { + rust("|ty| ty.0.clone()") + } + }, + ) +} /** * Config customization for a config param with no special behavior: @@ -104,17 +190,26 @@ data class ConfigParam(val name: String, val type: Symbol, val setterDocs: Writa * 2. convenience setter (non-optional) * 3. standard setter (&mut self) */ -fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : ConfigCustomization() { +fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext): ConfigCustomization = object : ConfigCustomization() { + private val runtimeMode = codegenContext.smithyRuntimeMode + override fun section(section: ServiceConfig): Writable { return when (section) { - is ServiceConfig.ConfigStruct -> writable { - docsOrFallback(param.getterDocs) - rust("pub (crate) ${param.name}: #T,", param.type.makeOptional()) + ServiceConfig.ConfigStruct -> writable { + if (runtimeMode.generateMiddleware) { + docsOrFallback(param.getterDocs) + val t = when (param.optional) { + true -> param.type.makeOptional() + false -> param.type + } + rust("pub (crate) ${param.name}: #T,", t) + } } - ServiceConfig.ConfigImpl -> emptySection ServiceConfig.BuilderStruct -> writable { - rust("${param.name}: #T,", param.type.makeOptional()) + if (runtimeMode.generateMiddleware) { + rust("${param.name}: #T,", param.type.makeOptional()) + } } ServiceConfig.BuilderImpl -> writable { @@ -122,26 +217,42 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf rust( """ pub fn ${param.name}(mut self, ${param.name}: impl Into<#T>) -> Self { - self.${param.name} = Some(${param.name}.into()); + self.set_${param.name}(Some(${param.name}.into())); self - }""", + }""", param.type, ) docsOrFallback(param.setterDocs) - rust( - """ - pub fn set_${param.name}(&mut self, ${param.name}: Option<#T>) -> &mut Self { - self.${param.name} = ${param.name}; - self - } - """, - param.type, - ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + pub fn set_${param.name}(&mut self, ${param.name}: Option<#{T}>) -> &mut Self { + self.config.store_or_unset(${param.name}.map(#{newtype})); + self + } + """, + "T" to param.type, + "newtype" to param.newtype!!, + ) + } else { + rust( + """ + pub fn set_${param.name}(&mut self, ${param.name}: Option<#T>) -> &mut Self { + self.${param.name} = ${param.name}; + self + } + """, + param.type, + ) + } } ServiceConfig.BuilderBuild -> writable { - rust("${param.name}: self.${param.name},") + if (runtimeMode.generateMiddleware) { + val default = "".letIf(!param.optional) { ".unwrap_or_default() " } + rust("${param.name}: self.${param.name}$default,") + } } else -> emptySection @@ -175,42 +286,85 @@ typealias ConfigCustomization = NamedCustomization * // builder implementation * } */ -class ServiceConfigGenerator(private val customizations: List = listOf()) { +class ServiceConfigGenerator( + codegenContext: ClientCodegenContext, + private val customizations: List = listOf(), +) { companion object { fun withBaseBehavior( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, extraCustomizations: List, ): ServiceConfigGenerator { val baseFeatures = mutableListOf() if (codegenContext.serviceShape.needsIdempotencyToken(codegenContext.model)) { - baseFeatures.add(IdempotencyTokenProviderCustomization()) + baseFeatures.add(IdempotencyTokenProviderCustomization(codegenContext)) } - return ServiceConfigGenerator(baseFeatures + extraCustomizations) + return ServiceConfigGenerator(codegenContext, baseFeatures + extraCustomizations) } } + private val moduleUseName = codegenContext.moduleUseName() + private val runtimeMode = codegenContext.smithyRuntimeMode + private val runtimeConfig = codegenContext.runtimeConfig + private val enableUserConfigurableRuntimePlugins = codegenContext.enableUserConfigurableRuntimePlugins + private val smithyTypes = RuntimeType.smithyTypes(runtimeConfig) + val codegenScope = arrayOf( + *preludeScope, + "BoxError" to RuntimeType.boxError(runtimeConfig), + "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), + "Cow" to RuntimeType.Cow, + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "SharedRuntimePlugin" to RuntimeType.sharedRuntimePlugin(runtimeConfig), + "runtime_plugin" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin"), + ) + fun render(writer: RustWriter) { - writer.docs("Service config.\n") + writer.docs("Configuration for a $moduleUseName service client.\n") customizations.forEach { it.section(ServiceConfig.ConfigStructAdditionalDocs)(writer) } + Attribute(Attribute.derive(RuntimeType.Clone)).render(writer) + if (runtimeMode.generateOrchestrator) { + Attribute(Attribute.derive(RuntimeType.Debug)).render(writer) + } writer.rustBlock("pub struct Config") { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + // Both `config` and `cloneable` are the same config, but the cloneable one + // is kept around so that it is possible to convert back into a builder. This can be + // optimized in the future. + pub(crate) config: #{FrozenLayer}, + cloneable: #{CloneableLayer}, + pub(crate) runtime_components: #{RuntimeComponentsBuilder}, + pub(crate) runtime_plugins: #{Vec}<#{SharedRuntimePlugin}>, + """, + *codegenScope, + ) + } customizations.forEach { it.section(ServiceConfig.ConfigStruct)(this) } } - // Custom implementation for Debug so we don't need to enforce Debug down the chain - writer.rustBlock("impl std::fmt::Debug for Config") { - writer.rustTemplate( - """ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut config = f.debug_struct("Config"); - config.finish() - } - """, - ) + if (runtimeMode.generateMiddleware) { + // Custom implementation for Debug so we don't need to enforce Debug down the chain + writer.rustBlock("impl std::fmt::Debug for Config") { + writer.rustTemplate( + """ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut config = f.debug_struct("Config"); + config.finish() + } + """, + ) + } } writer.rustBlock("impl Config") { @@ -220,25 +374,111 @@ class ServiceConfigGenerator(private val customizations: List Builder { Builder::default() } """, ) + if (runtimeMode.generateOrchestrator) { + writer.rustTemplate( + """ + /// Converts this config back into a builder so that it can be tweaked. + pub fn to_builder(&self) -> Builder { + Builder { + config: self.cloneable.clone(), + runtime_components: self.runtime_components.clone(), + runtime_plugins: self.runtime_plugins.clone(), + } + } + """, + ) + } customizations.forEach { it.section(ServiceConfig.ConfigImpl)(this) } } writer.docs("Builder for creating a `Config`.") - writer.raw("#[derive(Default)]") + if (runtimeMode.generateMiddleware) { + Attribute(Attribute.derive(RuntimeType.Clone, RuntimeType.Default)).render(writer) + } else { + Attribute(Attribute.derive(RuntimeType.Clone, RuntimeType.Debug)).render(writer) + } writer.rustBlock("pub struct Builder") { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + pub(crate) config: #{CloneableLayer}, + pub(crate) runtime_components: #{RuntimeComponentsBuilder}, + pub(crate) runtime_plugins: #{Vec}<#{SharedRuntimePlugin}>, + """, + *codegenScope, + ) + } customizations.forEach { it.section(ServiceConfig.BuilderStruct)(this) } } + + if (runtimeMode.generateMiddleware) { + // Custom implementation for Debug so we don't need to enforce Debug down the chain + writer.rustBlock("impl std::fmt::Debug for Builder") { + writer.rustTemplate( + """ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut config = f.debug_struct("Builder"); + config.finish() + } + """, + ) + } + } else { + // Custom implementation of Default to give the runtime components builder a name + writer.rustBlockTemplate("impl #{Default} for Builder", *codegenScope) { + writer.rustTemplate( + """ + fn default() -> Self { + Self { + config: #{Default}::default(), + runtime_components: #{RuntimeComponentsBuilder}::new("service config"), + runtime_plugins: #{Default}::default(), + } + } + """, + *codegenScope, + ) + } + } + writer.rustBlock("impl Builder") { writer.docs("Constructs a config builder.") - writer.rustTemplate("pub fn new() -> Self { Self::default() }") + writer.rust("pub fn new() -> Self { Self::default() }") customizations.forEach { it.section(ServiceConfig.BuilderImpl)(this) } + if (runtimeMode.generateOrchestrator) { + val visibility = if (enableUserConfigurableRuntimePlugins) { "pub" } else { "pub(crate)" } + + docs("Adds a runtime plugin to the config.") + if (!enableUserConfigurableRuntimePlugins) { Attribute.AllowUnused.render(this) } + rustTemplate( + """ + $visibility fn runtime_plugin(mut self, plugin: impl #{RuntimePlugin} + 'static) -> Self { + self.push_runtime_plugin(#{SharedRuntimePlugin}::new(plugin)); + self + } + """, + *codegenScope, + ) + docs("Adds a runtime plugin to the config.") + if (!enableUserConfigurableRuntimePlugins) { Attribute.AllowUnused.render(this) } + rustTemplate( + """ + $visibility fn push_runtime_plugin(&mut self, plugin: #{SharedRuntimePlugin}) -> &mut Self { + self.runtime_plugins.push(plugin); + self + } + """, + *codegenScope, + ) + } + val testUtilOnly = Attribute(Attribute.cfg(Attribute.any(Attribute.feature(TestUtilFeature.name), writable("test")))) @@ -258,16 +498,47 @@ class ServiceConfigGenerator(private val customizations: List Config") { - rustBlock("Config") { + if (runtimeMode.generateOrchestrator) { + rust("##[allow(unused_mut)]") + rustBlock("pub fn build(mut self) -> Config") { + rustTemplate( + """ + let mut layer = self.config; + let mut resolver = #{Resolver}::initial(&mut layer, &mut self.runtime_components); + """, + *codegenScope, + ) customizations.forEach { it.section(ServiceConfig.BuilderBuild)(this) } + rustBlock("Config") { + customizations.forEach { + it.section(ServiceConfig.BuilderBuildExtras)(this) + } + rustTemplate( + """ + config: #{Layer}::from(layer.clone()).with_name("$moduleUseName::config::Config").freeze(), + cloneable: layer, + runtime_components: self.runtime_components, + runtime_plugins: self.runtime_plugins, + """, + *codegenScope, + ) + } + } + } else { + rustBlock("pub fn build(self) -> Config") { + rustBlock("Config") { + customizations.forEach { + it.section(ServiceConfig.BuilderBuild)(this) + } + } } } - } - customizations.forEach { - it.section(ServiceConfig.Extras)(writer) + + customizations.forEach { + it.section(ServiceConfig.Extras)(writer) + } } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt index 26d926a78d..8e17d5bd9d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt @@ -22,10 +22,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.errorMetadata +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.Section @@ -107,14 +109,16 @@ class OperationErrorGenerator( ) } writer.rustBlock("impl #T for ${errorSymbol.name}", createUnhandledError) { - rustBlock( + rustBlockTemplate( """ fn create_unhandled_error( - source: Box, - meta: std::option::Option<#T> + source: #{Box}, + meta: #{Option}<#{ErrorMeta}> ) -> Self """, - errorMetadata, + *preludeScope, + "StdError" to RuntimeType.StdError, + "ErrorMeta" to errorMetadata, ) { rust( """ @@ -129,7 +133,7 @@ class OperationErrorGenerator( } } writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.Display) { - rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { + rustBlock("fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result") { delegateToVariants(errors) { writable { rust("_inner.fmt(f)") } } @@ -152,21 +156,28 @@ class OperationErrorGenerator( "impl #T for ${errorSymbol.name}", RuntimeType.provideErrorKind(symbolProvider.config.runtimeConfig), ) { - rustBlock("fn code(&self) -> std::option::Option<&str>") { + rustBlockTemplate("fn code(&self) -> #{Option}<&str>", *preludeScope) { rust("#T::code(self)", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) } - rustBlock("fn retryable_error_kind(&self) -> std::option::Option<#T>", retryErrorKindT) { + rustBlockTemplate( + "fn retryable_error_kind(&self) -> #{Option}<#{ErrorKind}>", + "ErrorKind" to retryErrorKindT, + *preludeScope, + ) { val retryableVariants = errors.filter { it.hasTrait() } if (retryableVariants.isEmpty()) { - rust("None") + rustTemplate("#{None}", *preludeScope) } else { rustBlock("match self") { retryableVariants.forEach { val errorVariantSymbol = symbolProvider.toSymbol(it) - rust("Self::${errorVariantSymbol.name}(inner) => Some(inner.retryable_error_kind()),") + rustTemplate( + "Self::${errorVariantSymbol.name}(inner) => #{Some}(inner.retryable_error_kind()),", + *preludeScope, + ) } - rust("_ => None") + rustTemplate("_ => #{None}", *preludeScope) } } } @@ -176,7 +187,7 @@ class OperationErrorGenerator( writer.rustTemplate( """ /// Creates the `${errorSymbol.name}::Unhandled` variant from any error type. - pub fn unhandled(err: impl Into>) -> Self { + pub fn unhandled(err: impl #{Into}<#{Box}>) -> Self { Self::Unhandled(#{Unhandled}::builder().source(err).build()) } @@ -185,8 +196,9 @@ class OperationErrorGenerator( Self::Unhandled(#{Unhandled}::builder().source(err.clone()).meta(err).build()) } """, + *preludeScope, "error_metadata" to errorMetadata, - "std_error" to RuntimeType.StdError, + "StdError" to RuntimeType.StdError, "Unhandled" to unhandledError(runtimeConfig), ) writer.docs( @@ -216,10 +228,14 @@ class OperationErrorGenerator( } writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.StdError) { - rustBlock("fn source(&self) -> std::option::Option<&(dyn #T + 'static)>", RuntimeType.StdError) { + rustBlockTemplate( + "fn source(&self) -> #{Option}<&(dyn #{StdError} + 'static)>", + *preludeScope, + "StdError" to RuntimeType.StdError, + ) { delegateToVariants(errors) { writable { - rust("Some(_inner)") + rustTemplate("#{Some}(_inner)", *preludeScope) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt index f461d59e05..c11fbfecf8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt @@ -80,7 +80,16 @@ class ServiceErrorGenerator( errors.map { it.id }, ) } - rust("impl #T for Error {}", RuntimeType.StdError) + rustBlock("impl #T for Error", RuntimeType.StdError) { + rustBlock("fn source(&self) -> std::option::Option<&(dyn #T + 'static)>", RuntimeType.StdError) { + rustBlock("match self") { + allErrors.forEach { + rust("Error::${symbolProvider.toSymbol(it).name}(inner) => inner.source(),") + } + rust("Error::Unhandled(inner) => inner.source()") + } + } + } writeCustomizations(customizations, ErrorSection.ServiceErrorAdditionalTraitImpls(allErrors)) } crate.lib { rust("pub use error_meta::Error;") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGenerator.kt index 148a09d4bd..0a4e5fb09b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGenerator.kt @@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.generators.OperationBuildError import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError @@ -71,6 +72,7 @@ class RequestBindingGenerator( private val encoder = RuntimeType.smithyTypes(runtimeConfig).resolve("primitive::Encoder") private val codegenScope = arrayOf( + *preludeScope, "BuildError" to runtimeConfig.operationBuildError(), "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, "Input" to symbolProvider.toSymbol(inputShape), @@ -90,11 +92,11 @@ class RequestBindingGenerator( fn update_http_builder( input: &#{Input}, builder: #{HttpRequestBuilder} - ) -> std::result::Result<#{HttpRequestBuilder}, #{BuildError}> + ) -> #{Result}<#{HttpRequestBuilder}, #{BuildError}> """, *codegenScope, ) { - write("let mut uri = String::new();") + rustTemplate("let mut uri = #{String}::new();", *preludeScope) write("uri_base(input, &mut uri)?;") if (hasQuery) { write("uri_query(input, &mut uri)?;") @@ -107,7 +109,7 @@ class RequestBindingGenerator( addHeadersFn, ) } - write("Ok(builder.method(${httpTrait.method.dq()}).uri(uri))") + rustTemplate("#{Ok}(builder.method(${httpTrait.method.dq()}).uri(uri))", *preludeScope) } } @@ -126,7 +128,7 @@ class RequestBindingGenerator( } val combinedArgs = listOf(formatString, *args.toTypedArray()) writer.rustBlockTemplate( - "fn uri_base(_input: &#{Input}, output: &mut String) -> std::result::Result<(), #{BuildError}>", + "fn uri_base(_input: &#{Input}, output: &mut #{String}) -> #{Result}<(), #{BuildError}>", *codegenScope, ) { rust("use #T as _;", RuntimeType.stdFmt.resolve("Write")) @@ -134,8 +136,8 @@ class RequestBindingGenerator( val member = inputShape.expectMember(label.content) serializeLabel(member, label, local(member)) } - rust("""write!(output, ${combinedArgs.joinToString(", ")}).expect("formatting should succeed");""") - rust("Ok(())") + rust("""::std::write!(output, ${combinedArgs.joinToString(", ")}).expect("formatting should succeed");""") + rustTemplate("#{Ok}(())", *codegenScope) } } @@ -165,7 +167,7 @@ class RequestBindingGenerator( } val preloadedParams = literalParams.keys + dynamicParams.map { it.locationName } writer.rustBlockTemplate( - "fn uri_query(_input: &#{Input}, mut output: &mut String) -> Result<(), #{BuildError}>", + "fn uri_query(_input: &#{Input}, mut output: &mut #{String}) -> #{Result}<(), #{BuildError}>", *codegenScope, ) { write("let mut query = #T::new(output);", RuntimeType.queryFormat(runtimeConfig, "Writer")) @@ -212,6 +214,7 @@ class RequestBindingGenerator( if (memberShape.isRequired) { val codegenScope = arrayOf( + *preludeScope, "BuildError" to OperationBuildError(runtimeConfig).missingField( memberName, "cannot be empty or unset", @@ -229,7 +232,7 @@ class RequestBindingGenerator( // Strings that aren't enums must be checked to see if they're empty if (target.isStringShape && !target.hasTrait()) { rustBlock("if $derefName.is_empty()") { - rustTemplate("return Err(#{BuildError:W});", *codegenScope) + rustTemplate("return #{Err}(#{BuildError:W});", *codegenScope) } } @@ -241,7 +244,7 @@ class RequestBindingGenerator( } } } - writer.rust("Ok(())") + writer.rustTemplate("#{Ok}(())", *codegenScope) } return true } @@ -329,9 +332,10 @@ class RequestBindingGenerator( rustTemplate( """ if $outputVar.is_empty() { - return Err(#{buildError:W}) + return #{Err}(#{buildError:W}) } """, + *preludeScope, "buildError" to buildError, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt deleted file mode 100644 index de19834721..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol - -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginGenerator -import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator -import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.rustlang.implBlock -import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.util.inputShape - -open class ClientProtocolGenerator( - private val codegenContext: ClientCodegenContext, - private val protocol: Protocol, - /** - * Operations generate a `make_operation(&config)` method to build a `aws_smithy_http::Operation` that can be dispatched - * This is the serializer side of request dispatch - */ - // TODO(enableNewSmithyRuntime): Remove the `makeOperationGenerator` - private val makeOperationGenerator: MakeOperationGenerator, - private val bodyGenerator: ProtocolPayloadGenerator, - // TODO(enableNewSmithyRuntime): Remove the `traitGenerator` - private val traitGenerator: HttpBoundProtocolTraitImplGenerator, -) : ProtocolGenerator(codegenContext, protocol) { - /** - * Render all code required for serializing requests and deserializing responses for the operation - * - * This primarily relies on two components: - * 1. [traitGenerator]: Generate implementations of the `ParseHttpResponse` trait for the operations - * 2. [makeOperationGenerator]: Generate the `make_operation()` method which is used to serialize operations - * to HTTP requests - */ - fun renderOperation( - operationWriter: RustWriter, - // TODO(enableNewSmithyRuntime): Remove the `inputWriter` since `make_operation` generation is going away - inputWriter: RustWriter, - operationShape: OperationShape, - customizations: List, - ) { - val inputShape = operationShape.inputShape(model) - - // impl OperationInputShape { ... } - inputWriter.implBlock(symbolProvider.toSymbol(inputShape)) { - writeCustomizations( - customizations, - OperationSection.InputImpl(customizations, operationShape, inputShape, protocol), - ) - makeOperationGenerator.generateMakeOperation(this, operationShape, customizations) - } - - renderOperationStruct(operationWriter, operationShape, customizations) - } - - private fun renderOperationStruct( - operationWriter: RustWriter, - operationShape: OperationShape, - customizations: List, - ) { - val operationName = symbolProvider.toSymbol(operationShape).name - - // pub struct Operation { ... } - operationWriter.rust( - """ - /// `ParseStrictResponse` impl for `$operationName`. - """, - ) - Attribute(derive(RuntimeType.Clone, RuntimeType.Default, RuntimeType.Debug)).render(operationWriter) - Attribute.NonExhaustive.render(operationWriter) - Attribute.DocHidden.render(operationWriter) - operationWriter.rust("pub struct $operationName;") - operationWriter.implBlock(symbolProvider.toSymbol(operationShape)) { - Attribute.DocHidden.render(operationWriter) - rustBlock("pub fn new() -> Self") { - rust("Self") - } - - writeCustomizations(customizations, OperationSection.OperationImplBlock(customizations)) - } - traitGenerator.generateTraitImpls(operationWriter, operationShape, customizations) - - if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { - OperationRuntimePluginGenerator(codegenContext).render(operationWriter, operationName) - - ResponseDeserializerGenerator(codegenContext, protocol) - .render(operationWriter, operationShape, customizations) - RequestSerializerGenerator(codegenContext, protocol, bodyGenerator) - .render(operationWriter, operationShape, customizations) - } - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt index 5d206d47e8..d79817df40 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt @@ -5,11 +5,13 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol -import software.amazon.smithy.aws.traits.ServiceTrait import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.http.RequestBindingGenerator +import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientAdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.docs @@ -20,8 +22,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator @@ -29,11 +30,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.findStreamingMember -import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rust.codegen.core.util.sdkId -// TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` +// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up `enableNewSmithyRuntime` /** Generates the `make_operation` function on input structs */ open class MakeOperationGenerator( protected val codegenContext: CodegenContext, @@ -51,18 +52,19 @@ open class MakeOperationGenerator( private val defaultClassifier = RuntimeType.smithyHttp(runtimeConfig) .resolve("retry::DefaultResponseRetryClassifier") - private val sdkId = - codegenContext.serviceShape.getTrait()?.sdkId?.lowercase()?.replace(" ", "") - ?: codegenContext.serviceShape.id.getName(codegenContext.serviceShape) + private val sdkId = codegenContext.serviceShape.sdkId() private val codegenScope = arrayOf( - "config" to ClientRustModule.Config, + *preludeScope, + "config" to ClientRustModule.config, "header_util" to RuntimeType.smithyHttp(runtimeConfig).resolve("header"), "http" to RuntimeType.Http, + "operation" to RuntimeType.operationModule(runtimeConfig), "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, "OpBuildError" to runtimeConfig.operationBuildError(), - "operation" to RuntimeType.operationModule(runtimeConfig), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), + "SharedPropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::SharedPropertyBag"), + "RetryMode" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryMode"), ) fun generateMakeOperation( @@ -73,7 +75,7 @@ open class MakeOperationGenerator( val operationName = symbolProvider.toSymbol(shape).name val baseReturnType = buildOperationType(implBlockWriter, shape, customizations) val returnType = - "std::result::Result<$baseReturnType, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" + "#{Result}<$baseReturnType, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" val outputSymbol = symbolProvider.toSymbol(shape) val takesOwnership = bodyGenerator.payloadMetadata(shape).takesOwnership @@ -94,18 +96,29 @@ open class MakeOperationGenerator( "$fnType $functionName($self, _config: &#{config}::Config) -> $returnType", *codegenScope, ) { + rustTemplate( + """ + assert_ne!(_config.retry_config().map(|rc| rc.mode()), #{Option}::Some(#{RetryMode}::Adaptive), "Adaptive retry mode is unsupported, please use Standard mode or disable retries."); + """, + *codegenScope, + ) writeCustomizations(customizations, OperationSection.MutateInput(customizations, "self", "_config")) withBlock("let mut request = {", "};") { createHttpRequest(this, shape) } - rust("let mut properties = aws_smithy_http::property_bag::SharedPropertyBag::new();") + rustTemplate("let mut properties = #{SharedPropertyBag}::new();", *codegenScope) // When the payload is a `ByteStream`, `into_inner()` already returns an `SdkBody`, so we mute this // Clippy warning to make the codegen a little simpler in that case. Attribute.AllowClippyUselessConversion.render(this) withBlockTemplate("let body = #{SdkBody}::from(", ");", *codegenScope) { - bodyGenerator.generatePayload(this, "self", shape) + bodyGenerator.generatePayload( + this, + "self", + shape, + ClientAdditionalPayloadContext(propertyBagAvailable = true), + ) val streamingMember = shape.inputShape(model).findStreamingMember(model) val isBlobStreaming = streamingMember != null && model.expectShape(streamingMember.target) is BlobShape if (isBlobStreaming) { @@ -116,7 +129,7 @@ open class MakeOperationGenerator( if (includeDefaultPayloadHeaders && needsContentLength(shape)) { rustTemplate( """ - if let Some(content_length) = body.content_length() { + if let #{Some}(content_length) = body.content_length() { request = #{header_util}::set_request_header_if_absent(request, #{http}::header::CONTENT_LENGTH, content_length); } """, @@ -140,7 +153,7 @@ open class MakeOperationGenerator( "OperationType" to symbolProvider.toSymbol(shape), ) writeCustomizations(customizations, OperationSection.FinalizeOperation(customizations, "op", "_config")) - rust("Ok(op)") + rustTemplate("#{Ok}(op)", *codegenScope) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt index 616325ee3e..61b0c42047 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt @@ -10,6 +10,8 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.http.ResponseBindingGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -23,8 +25,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName @@ -55,15 +55,21 @@ class ProtocolParserGenerator( "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), "Bytes" to RuntimeType.Bytes, "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), - // TODO(enableNewSmithyRuntime): Remove the `PropertyBag` below + // TODO(enableNewSmithyRuntimeCleanup): Remove the `PropertyBag` below "PropertyBag" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("property_bag::PropertyBag"), ) - fun parseResponseFn(operationShape: OperationShape, customizations: List): RuntimeType { + fun parseResponseFn( + operationShape: OperationShape, + // TODO(enableNewSmithyRuntimeCleanup): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + propertyBagAvailable: Boolean, + customizations: List, + ): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) val errorSymbol = symbolProvider.symbolForOperationError(operationShape) - return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "http_response") { fnName -> + val fnNameSuffix = if (propertyBagAvailable) "http_response_with_props" else "http_response" + return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = fnNameSuffix) { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) rustBlockTemplate( "pub fn $fnName(_response_status: u16, _response_headers: &#{http}::header::HeaderMap, _response_body: &[u8]) -> std::result::Result<#{O}, #{E}>", @@ -77,6 +83,7 @@ class ProtocolParserGenerator( outputShape, httpBindingResolver.responseBindings(operationShape), errorSymbol, + propertyBagAvailable, customizations, ) } @@ -84,7 +91,10 @@ class ProtocolParserGenerator( } } - fun parseErrorFn(operationShape: OperationShape, customizations: List): RuntimeType { + fun parseErrorFn( + operationShape: OperationShape, + customizations: List, + ): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) val errorSymbol = symbolProvider.symbolForOperationError(operationShape) @@ -143,6 +153,7 @@ class ProtocolParserGenerator( errorShape, httpBindingResolver.errorResponseBindings(errorShape), errorSymbol, + false, listOf( object : OperationCustomization() { override fun section(section: OperationSection): Writable = { @@ -178,17 +189,17 @@ class ProtocolParserGenerator( fun parseStreamingResponseFn( operationShape: OperationShape, - // TODO(enableNewSmithyRuntime): Remove the `includeProperties` flag as if it were always set to `false` - includeProperties: Boolean, + // TODO(enableNewSmithyRuntimeCleanup): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + propertyBagAvailable: Boolean, customizations: List, ): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) val errorSymbol = symbolProvider.symbolForOperationError(operationShape) - val fnNameSuffix = if (includeProperties) "http_response_with_props" else "http_response" + val fnNameSuffix = if (propertyBagAvailable) "http_response_with_props" else "http_response" return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = fnNameSuffix) { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) - val propertiesArg = if (includeProperties) { + val propertiesArg = if (propertyBagAvailable) { Attribute.AllowUnusedVariables.render(this) ", properties: &#{PropertyBag}" } else { @@ -217,6 +228,7 @@ class ProtocolParserGenerator( outputShape, httpBindingResolver.responseBindings(operationShape), errorSymbol, + propertyBagAvailable, customizations, ) } @@ -229,10 +241,12 @@ class ProtocolParserGenerator( outputShape: StructureShape, bindings: List, errorSymbol: Symbol, + // TODO(enableNewSmithyRuntimeCleanup): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + propertyBagAvailable: Boolean, customizations: List, ) { val httpBindingGenerator = ResponseBindingGenerator(protocol, codegenContext, operationShape) - val structuredDataParser = protocol.structuredDataParser(operationShape) + val structuredDataParser = protocol.structuredDataParser() Attribute.AllowUnusedMut.render(this) rust("let mut output = #T::default();", symbolProvider.symbolForBuilder(outputShape)) if (outputShape.id == operationShape.output.get()) { @@ -270,7 +284,7 @@ class ProtocolParserGenerator( writeCustomizations( customizations, - OperationSection.MutateOutput(customizations, operationShape, "_response_headers"), + OperationSection.MutateOutput(customizations, operationShape, "_response_headers", propertyBagAvailable), ) rust("output.build()$err") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 5a1c4993b0..2f994788fe 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -20,9 +20,11 @@ import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase import software.amazon.smithy.protocoltests.traits.HttpResponseTestsTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule -import software.amazon.smithy.rust.codegen.client.smithy.generators.clientInstantiator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.EndpointPrefixGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.allow +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -32,7 +34,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait @@ -43,16 +44,63 @@ import software.amazon.smithy.rust.codegen.core.util.orNull import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import java.util.logging.Logger +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType as RT + +data class ClientCreationParams( + val codegenContext: ClientCodegenContext, + val connectorName: String, + val configBuilderName: String, + val clientName: String, +) + +interface ProtocolTestGenerator { + val codegenContext: ClientCodegenContext + val protocolSupport: ProtocolSupport + val operationShape: OperationShape + + fun render(writer: RustWriter) +} /** * Generate protocol tests for an operation */ -class ProtocolTestGenerator( - private val codegenContext: ClientCodegenContext, - private val protocolSupport: ProtocolSupport, - private val operationShape: OperationShape, - private val writer: RustWriter, -) { +class DefaultProtocolTestGenerator( + override val codegenContext: ClientCodegenContext, + override val protocolSupport: ProtocolSupport, + override val operationShape: OperationShape, + + private val renderClientCreation: RustWriter.(ClientCreationParams) -> Unit = { params -> + if (params.codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + let smithy_client = #{Builder}::new() + .connector(${params.connectorName}) + .middleware(#{MapRequestLayer}::for_mapper(#{SmithyEndpointStage}::new())) + .build(); + let ${params.clientName} = #{Client}::with_config(smithy_client, ${params.configBuilderName}.build()); + """, + "Client" to ClientRustModule.root.toType().resolve("Client"), + "Builder" to ClientRustModule.client.toType().resolve("Builder"), + "SmithyEndpointStage" to RT.smithyHttp(codegenContext.runtimeConfig) + .resolve("endpoint::middleware::SmithyEndpointStage"), + "MapRequestLayer" to RT.smithyHttpTower(codegenContext.runtimeConfig) + .resolve("map_request::MapRequestLayer"), + ) + } else { + rustTemplate( + """ + let ${params.clientName} = #{Client}::from_conf( + ${params.configBuilderName} + .http_connector(${params.connectorName}) + .build() + ); + """, + "Client" to ClientRustModule.root.toType().resolve("Client"), + ) + } + }, +) : ProtocolTestGenerator { + private val rc = codegenContext.runtimeConfig private val logger = Logger.getLogger(javaClass.name) private val inputShape = operationShape.inputShape(codegenContext.model) @@ -60,11 +108,11 @@ class ProtocolTestGenerator( private val operationSymbol = codegenContext.symbolProvider.toSymbol(operationShape) private val operationIndex = OperationIndex.of(codegenContext.model) - private val instantiator = clientInstantiator(codegenContext) + private val instantiator = ClientInstantiator(codegenContext) private val codegenScope = arrayOf( - "SmithyHttp" to RuntimeType.smithyHttp(codegenContext.runtimeConfig), - "AssertEq" to RuntimeType.PrettyAssertions.resolve("assert_eq!"), + "SmithyHttp" to RT.smithyHttp(rc), + "AssertEq" to RT.PrettyAssertions.resolve("assert_eq!"), ) sealed class TestCase { @@ -75,7 +123,7 @@ class ProtocolTestGenerator( TestCase() } - fun render() { + override fun render(writer: RustWriter) { val requestTests = operationShape.getTrait() ?.getTestCasesFor(AppliesTo.CLIENT).orEmpty().map { TestCase.RequestTest(it) } val responseTests = operationShape.getTrait() @@ -150,6 +198,7 @@ class ProtocolTestGenerator( is Action.Response -> "_response" is Action.Request -> "_request" } + Attribute.AllowUnusedMut.render(testModuleWriter) testModuleWriter.rustBlock("async fn ${testCase.id.toSnakeCase()}$fnName()") { block(this) } @@ -166,33 +215,58 @@ class ProtocolTestGenerator( writable { val customizations = codegenContext.rootDecorator.endpointCustomizations(codegenContext) params.getObjectMember("builtInParams").orNull()?.members?.forEach { (name, value) -> - customizations.firstNotNullOf { it.setBuiltInOnServiceConfig(name.value, value, "builder") }(this) + customizations.firstNotNullOf { + it.setBuiltInOnServiceConfig(name.value, value, "config_builder") + }(this) } } } ?: writable { } rustTemplate( """ - let builder = #{config}::Config::builder().with_test_defaults().endpoint_resolver("https://example.com"); + let (conn, request_receiver) = #{capture_request}(None); + let config_builder = #{config}::Config::builder().with_test_defaults().endpoint_resolver("https://example.com"); #{customParams} - let config = builder.build(); """, - "config" to ClientRustModule.Config, + "capture_request" to CargoDependency.smithyClient(rc) + .toDevDependency() + .withFeature("test-util") + .toType() + .resolve("test_connection::capture_request"), + "config" to ClientRustModule.config, "customParams" to customParams, ) - writeInline("let input =") - instantiator.render(this, inputShape, httpRequestTestCase.params) + renderClientCreation(this, ClientCreationParams(codegenContext, "conn", "config_builder", "client")) + + writeInline("let result = ") + instantiator.renderFluentCall(this, "client", operationShape, inputShape, httpRequestTestCase.params) + rust(""".send().await;""") + // Response parsing will always fail since we feed it an empty response body, so we don't care + // if it fails, but it is helpful to print what that failure was for debugging + rust("let _ = dbg!(result);") + rust("""let http_request = request_receiver.expect_request();""") - rust(""".make_operation(&config).await.expect("operation failed to build");""") - rust("let (http_request, parts) = input.into_request_response().0.into_parts();") with(httpRequestTestCase) { + // Override the endpoint for tests that set a `host`, for example: + // https://github.com/awslabs/smithy/blob/be68f3bbdfe5bf50a104b387094d40c8069f16b1/smithy-aws-protocol-tests/model/restJson1/endpoint-paths.smithy#L19 host.orNull()?.also { host -> val withScheme = "http://$host" + when (val bindings = EndpointPrefixGenerator.endpointTraitBindings(codegenContext, operationShape)) { + null -> rust("let endpoint_prefix = None;") + else -> { + withBlock("let input = ", ";") { + instantiator.render(this@renderHttpRequestTestCase, inputShape, httpRequestTestCase.params) + } + withBlock("let endpoint_prefix = Some({", "}.unwrap());") { + bindings.render(this, "input", codegenContext.smithyRuntimeMode, generateValidation = false) + } + } + } rustTemplate( """ let mut http_request = http_request; let ep = #{SmithyHttp}::endpoint::Endpoint::mutable(${withScheme.dq()}).expect("valid endpoint"); - ep.set_endpoint(http_request.uri_mut(), parts.acquire().get()).expect("valid endpoint"); + ep.set_endpoint(http_request.uri_mut(), endpoint_prefix.as_ref()).expect("valid endpoint"); """, *codegenScope, ) @@ -261,7 +335,7 @@ class ProtocolTestGenerator( writeInline("let expected_output =") instantiator.render(this, expectedShape, testCase.params) write(";") - write("let http_response = #T::new()", RuntimeType.HttpResponseBuilder) + write("let mut http_response = #T::new()", RT.HttpResponseBuilder) testCase.headers.forEach { (key, value) -> writeWithNoFormatting(".header(${key.dq()}, ${value.dq()})") } @@ -271,31 +345,64 @@ class ProtocolTestGenerator( .body(#T::from(${testCase.body.orNull()?.dq()?.replace("#", "##") ?: "vec![]"})) .unwrap(); """, - RuntimeType.sdkBody(runtimeConfig = codegenContext.runtimeConfig), - ) - write( - "let mut op_response = #T::new(http_response);", - RuntimeType.operationModule(codegenContext.runtimeConfig).resolve("Response"), - ) - rustTemplate( - """ - use #{parse_http_response}; - let parser = #{op}::new(); - let parsed = parser.parse_unloaded(&mut op_response); - let parsed = parsed.unwrap_or_else(|| { - let (http_response, _) = op_response.into_parts(); - let http_response = http_response.map(|body|#{copy_from_slice}(body.bytes().unwrap())); - <#{op} as #{parse_http_response}>::parse_loaded(&parser, &http_response) - }); - """, - "op" to operationSymbol, - "copy_from_slice" to RuntimeType.Bytes.resolve("copy_from_slice"), - "parse_http_response" to RuntimeType.parseHttpResponse(codegenContext.runtimeConfig), + RT.sdkBody(runtimeConfig = rc), ) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rust( + "let mut op_response = #T::new(http_response);", + RT.operationModule(rc).resolve("Response"), + ) + rustTemplate( + """ + use #{parse_http_response}; + let parser = #{op}::new(); + let parsed = parser.parse_unloaded(&mut op_response); + let parsed = parsed.unwrap_or_else(|| { + let (http_response, _) = op_response.into_parts(); + let http_response = http_response.map(|body|#{copy_from_slice}(body.bytes().unwrap())); + <#{op} as #{parse_http_response}>::parse_loaded(&parser, &http_response) + }); + """, + "op" to operationSymbol, + "copy_from_slice" to RT.Bytes.resolve("copy_from_slice"), + "parse_http_response" to RT.parseHttpResponse(rc), + ) + } else { + rustTemplate( + """ + use #{ResponseDeserializer}; + use #{RuntimePlugin}; + + let op = #{Operation}::new(); + let config = op.config().expect("the operation has config"); + let de = config.load::<#{SharedResponseDeserializer}>().expect("the config must have a deserializer"); + + let parsed = de.deserialize_streaming(&mut http_response); + let parsed = parsed.unwrap_or_else(|| { + let http_response = http_response.map(|body| { + #{SdkBody}::from(#{copy_from_slice}(body.bytes().unwrap())) + }); + de.deserialize_nonstreaming(&http_response) + }); + """, + "copy_from_slice" to RT.Bytes.resolve("copy_from_slice"), + "SharedResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::SharedResponseDeserializer"), + "Operation" to codegenContext.symbolProvider.toSymbol(operationShape), + "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::ResponseDeserializer"), + "RuntimePlugin" to RT.runtimePlugin(rc), + "SdkBody" to RT.sdkBody(rc), + ) + } if (expectedShape.hasTrait()) { val errorSymbol = codegenContext.symbolProvider.symbolForOperationError(operationShape) val errorVariant = codegenContext.symbolProvider.toSymbol(expectedShape).name rust("""let parsed = parsed.expect_err("should be error response");""") + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + """let parsed: &#{Error} = parsed.as_operation_error().expect("operation error").downcast_ref().unwrap();""", + "Error" to codegenContext.symbolProvider.symbolForOperationError(operationShape), + ) + } rustBlock("if let #T::$errorVariant(parsed) = parsed", errorSymbol) { compareMembers(expectedShape) } @@ -303,7 +410,14 @@ class ProtocolTestGenerator( rust("panic!(\"wrong variant: Got: {:?}. Expected: {:?}\", parsed, expected_output);") } } else { - rust("let parsed = parsed.unwrap();") + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rust("let parsed = parsed.unwrap();") + } else { + rustTemplate( + """let parsed = parsed.expect("should be successful response").downcast::<#{Output}>().unwrap();""", + "Output" to codegenContext.symbolProvider.toSymbol(expectedShape), + ) + } compareMembers(outputShape) } } @@ -324,9 +438,7 @@ class ProtocolTestGenerator( } else { when (codegenContext.model.expectShape(member.target)) { is DoubleShape, is FloatShape -> { - addUseImports( - RuntimeType.protocolTest(codegenContext.runtimeConfig, "FloatEquals").toSymbol(), - ) + addUseImports(RT.protocolTest(rc, "FloatEquals").toSymbol()) rust( """ assert!(parsed.$memberName.float_equals(&expected_output.$memberName), @@ -351,7 +463,7 @@ class ProtocolTestGenerator( rustWriter.rustTemplate( """ // No body - #{AssertEq}(std::str::from_utf8(body).unwrap(), ""); + #{AssertEq}(::std::str::from_utf8(body).unwrap(), ""); """, *codegenScope, ) @@ -362,8 +474,8 @@ class ProtocolTestGenerator( "#T(&body, ${ rustWriter.escape(body).dq() }, #T::from(${(mediaType ?: "unknown").dq()}))", - RuntimeType.protocolTest(codegenContext.runtimeConfig, "validate_body"), - RuntimeType.protocolTest(codegenContext.runtimeConfig, "MediaType"), + RT.protocolTest(rc, "validate_body"), + RT.protocolTest(rc, "MediaType"), ) } } @@ -404,7 +516,7 @@ class ProtocolTestGenerator( assertOk(rustWriter) { write( "#T($actualExpression, $variableName)", - RuntimeType.protocolTest(codegenContext.runtimeConfig, "validate_headers"), + RT.protocolTest(rc, "validate_headers"), ) } } @@ -458,7 +570,7 @@ class ProtocolTestGenerator( assertOk(rustWriter) { write( "#T($actualExpression, $expectedVariableName)", - RuntimeType.protocolTest(codegenContext.runtimeConfig, checkFunction), + RT.protocolTest(rc, checkFunction), ) } } @@ -468,7 +580,7 @@ class ProtocolTestGenerator( * for pretty printing protocol test helper results */ private fun assertOk(rustWriter: RustWriter, inner: Writable) { - rustWriter.write("#T(", RuntimeType.protocolTest(codegenContext.runtimeConfig, "assert_ok")) + rustWriter.write("#T(", RT.protocolTest(rc, "assert_ok")) inner(rustWriter) rustWriter.write(");") } @@ -502,6 +614,20 @@ class ProtocolTestGenerator( // These tests are not even attempted to be generated, either because they will not compile // or because they are flaky - private val DisableTests = setOf() + private val DisableTests = setOf( + // TODO(https://github.com/awslabs/smithy-rs/issues/2891): Implement support for `@requestCompression` + "SDKAppendedGzipAfterProvidedEncoding_restJson1", + "SDKAppendedGzipAfterProvidedEncoding_restXml", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_0", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_1", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsQuery", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_ec2Query", + "SDKAppliedContentEncoding_awsJson1_0", + "SDKAppliedContentEncoding_awsJson1_1", + "SDKAppliedContentEncoding_awsQuery", + "SDKAppliedContentEncoding_ec2Query", + "SDKAppliedContentEncoding_restJson1", + "SDKAppliedContentEncoding_restXml", + ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index 74cac8c60e..66b9d7e722 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -10,14 +10,15 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.generators.http.RequestBindingGenerator -import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientAdditionalPayloadContext +import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -28,48 +29,56 @@ import software.amazon.smithy.rust.codegen.core.util.inputShape class RequestSerializerGenerator( private val codegenContext: ClientCodegenContext, private val protocol: Protocol, - private val bodyGenerator: ProtocolPayloadGenerator, + private val bodyGenerator: ProtocolPayloadGenerator?, + private val nameOverride: String? = null, ) { private val httpBindingResolver = protocol.httpBindingResolver private val symbolProvider = codegenContext.symbolProvider private val codegenScope by lazy { - CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toType().let { runtimeApi -> - val interceptorContext = runtimeApi.resolve("client::interceptors::context") - val orchestrator = runtimeApi.resolve("client::orchestrator") - arrayOf( - "BoxError" to orchestrator.resolve("BoxError"), - "HttpRequest" to orchestrator.resolve("HttpRequest"), - "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, - "Input" to interceptorContext.resolve("Input"), - "RequestSerializer" to orchestrator.resolve("RequestSerializer"), - "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), - "TypedBox" to runtimeApi.resolve("type_erasure::TypedBox"), - "config" to ClientRustModule.Config, - "header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"), - "http" to RuntimeType.Http, - "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), - ) - } + val runtimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) + val interceptorContext = runtimeApi.resolve("client::interceptors::context") + val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) + arrayOf( + *preludeScope, + "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), + "config" to ClientRustModule.config, + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), + "header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"), + "http" to RuntimeType.Http, + "HttpRequest" to runtimeApi.resolve("client::orchestrator::HttpRequest"), + "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, + "Input" to interceptorContext.resolve("Input"), + "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), + "RequestSerializer" to runtimeApi.resolve("client::ser_de::RequestSerializer"), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "HeaderSerializationSettings" to RuntimeType.forInlineDependency( + InlineDependency.serializationSettings( + codegenContext.runtimeConfig, + ), + ).resolve("HeaderSerializationSettings"), + ) } - fun render(writer: RustWriter, operationShape: OperationShape, customizations: List) { + fun render(writer: RustWriter, operationShape: OperationShape) { val inputShape = operationShape.inputShape(codegenContext.model) val operationName = symbolProvider.toSymbol(operationShape).name val inputSymbol = symbolProvider.toSymbol(inputShape) + val serializerName = nameOverride ?: "${operationName}RequestSerializer" writer.rustTemplate( """ ##[derive(Debug)] - struct ${operationName}RequestSerializer; - impl #{RequestSerializer} for ${operationName}RequestSerializer { + struct $serializerName; + impl #{RequestSerializer} for $serializerName { ##[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] - fn serialize_input(&self, input: #{Input}) -> Result<#{HttpRequest}, #{BoxError}> { - let input = #{TypedBox}::<#{ConcreteInput}>::assume_from(input).expect("correct type").unwrap(); + fn serialize_input(&self, input: #{Input}, _cfg: &mut #{ConfigBag}) -> #{Result}<#{HttpRequest}, #{BoxError}> { + let input = input.downcast::<#{ConcreteInput}>().expect("correct type"); + let _header_serialization_settings = _cfg.load::<#{HeaderSerializationSettings}>().cloned().unwrap_or_default(); let mut request_builder = { #{create_http_request} }; let body = #{generate_body}; #{add_content_length} - Ok(request_builder.body(body).expect("valid request")) + #{Ok}(request_builder.body(body).expect("valid request")) } } """, @@ -77,15 +86,26 @@ class RequestSerializerGenerator( "ConcreteInput" to inputSymbol, "create_http_request" to createHttpRequest(operationShape), "generate_body" to writable { - val body = writable { bodyGenerator.generatePayload(this, "input", operationShape) } - val streamingMember = inputShape.findStreamingMember(codegenContext.model) - val isBlobStreaming = - streamingMember != null && codegenContext.model.expectShape(streamingMember.target) is BlobShape - if (isBlobStreaming) { - // Consume the `ByteStream` into its inner `SdkBody`. - rust("#T.into_inner()", body) + if (bodyGenerator != null) { + val body = writable { + bodyGenerator.generatePayload( + this, + "input", + operationShape, + ClientAdditionalPayloadContext(propertyBagAvailable = false), + ) + } + val streamingMember = inputShape.findStreamingMember(codegenContext.model) + val isBlobStreaming = + streamingMember != null && codegenContext.model.expectShape(streamingMember.target) is BlobShape + if (isBlobStreaming) { + // Consume the `ByteStream` into its inner `SdkBody`. + rust("#T.into_inner()", body) + } else { + rustTemplate("#{SdkBody}::from(#{body})", *codegenScope, "body" to body) + } } else { - rustTemplate("#{SdkBody}::from(#{body})", *codegenScope, "body" to body) + rustTemplate("#{SdkBody}::empty()", *codegenScope) } }, "add_content_length" to if (needsContentLength(operationShape)) { @@ -93,7 +113,8 @@ class RequestSerializerGenerator( rustTemplate( """ if let Some(content_length) = body.content_length() { - request_builder = #{header_util}::set_request_header_if_absent(request_builder, #{http}::header::CONTENT_LENGTH, content_length); + let content_length = content_length.to_string(); + request_builder = _header_serialization_settings.set_default_header(request_builder, #{http}::header::CONTENT_LENGTH, &content_length); } """, *codegenScope, @@ -119,17 +140,17 @@ class RequestSerializerGenerator( httpBindingGenerator.renderUpdateHttpBuilder(this) val contentType = httpBindingResolver.requestContentType(operationShape) - rust("let mut builder = update_http_builder(&input, #T::new())?;", RuntimeType.HttpRequestBuilder) + rustTemplate("let mut builder = update_http_builder(&input, #{HttpRequestBuilder}::new())?;", *codegenScope) if (contentType != null) { rustTemplate( - "builder = #{header_util}::set_request_header_if_absent(builder, #{http}::header::CONTENT_TYPE, ${contentType.dq()});", + "builder = _header_serialization_settings.set_default_header(builder, #{http}::header::CONTENT_TYPE, ${contentType.dq()});", *codegenScope, ) } for (header in protocol.additionalRequestHeaders(operationShape)) { rustTemplate( """ - builder = #{header_util}::set_request_header_if_absent( + builder = _header_serialization_settings.set_default_header( builder, #{http}::header::HeaderName::from_static(${header.first.dq()}), ${header.second.dq()} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 81c0494c17..accd436e42 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -7,13 +7,14 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions @@ -36,15 +37,16 @@ class ResponseDeserializerGenerator( val orchestrator = CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::orchestrator") arrayOf( + *preludeScope, "Error" to interceptorContext.resolve("Error"), "HttpResponse" to orchestrator.resolve("HttpResponse"), "Instrument" to CargoDependency.Tracing.toType().resolve("Instrument"), "Output" to interceptorContext.resolve("Output"), "OutputOrError" to interceptorContext.resolve("OutputOrError"), - "ResponseDeserializer" to orchestrator.resolve("ResponseDeserializer"), + "OrchestratorError" to orchestrator.resolve("OrchestratorError"), + "ResponseDeserializer" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::ser_de::ResponseDeserializer"), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "SdkError" to RuntimeType.sdkError(runtimeConfig), - "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), "debug_span" to RuntimeType.Tracing.resolve("debug_span"), "type_erase_result" to typeEraseResult(), ) @@ -91,14 +93,14 @@ class ResponseDeserializerGenerator( val successCode = httpBindingResolver.httpTrait(operationShape).code rustTemplate( """ - fn deserialize_streaming(&self, response: &mut #{HttpResponse}) -> Option<#{OutputOrError}> { + fn deserialize_streaming(&self, response: &mut #{HttpResponse}) -> #{Option}<#{OutputOrError}> { #{BeforeParseResponse} // If this is an error, defer to the non-streaming parser if !response.status().is_success() && response.status().as_u16() != $successCode { - return None; + return #{None}; } - Some(#{type_erase_result}(#{parse_streaming_response}(response))) + #{Some}(#{type_erase_result}(#{parse_streaming_response}(response))) } """, *codegenScope, @@ -134,7 +136,7 @@ class ResponseDeserializerGenerator( let (success, status) = (response.status().is_success(), response.status().as_u16()); let headers = response.headers(); let body = response.body().bytes().expect("body loaded"); - #{BeforeParseResponse} + #{BeforeParseResponse} let parse_result = if !success && status != $successCode { #{parse_error}(status, headers, body) } else { @@ -144,7 +146,7 @@ class ResponseDeserializerGenerator( """, *codegenScope, "parse_error" to parserGenerator.parseErrorFn(operationShape, customizations), - "parse_response" to parserGenerator.parseResponseFn(operationShape, customizations), + "parse_response" to parserGenerator.parseResponseFn(operationShape, false, customizations), "BeforeParseResponse" to writable { writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) }, @@ -154,13 +156,14 @@ class ResponseDeserializerGenerator( private fun typeEraseResult(): RuntimeType = ProtocolFunctions.crossOperationFn("type_erase_result") { fnName -> rustTemplate( """ - pub(crate) fn $fnName(result: Result) -> Result<#{Output}, #{Error}> + pub(crate) fn $fnName(result: #{Result}) -> #{Result}<#{Output}, #{OrchestratorError}<#{Error}>> where - O: Send + Sync + 'static, - E: Send + Sync + 'static, + O: ::std::fmt::Debug + #{Send} + #{Sync} + 'static, + E: ::std::error::Error + std::fmt::Debug + #{Send} + #{Sync} + 'static, { - result.map(|output| #{TypedBox}::new(output).erase()) - .map_err(|error| #{TypedBox}::new(error).erase()) + result.map(|output| #{Output}::erase(output)) + .map_err(|error| #{Error}::erase(error)) + .map_err(#{Into}::into) } """, *codegenScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/ClientProtocolLoader.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/ClientProtocolLoader.kt index a194dc98a2..7c949c92a2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/ClientProtocolLoader.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/ClientProtocolLoader.kt @@ -14,7 +14,7 @@ import software.amazon.smithy.aws.traits.protocols.RestJson1Trait import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJson @@ -30,8 +30,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml import software.amazon.smithy.rust.codegen.core.util.hasTrait -class ClientProtocolLoader(supportedProtocols: ProtocolMap) : - ProtocolLoader(supportedProtocols) { +class ClientProtocolLoader(supportedProtocols: ProtocolMap) : + ProtocolLoader(supportedProtocols) { companion object { val DefaultProtocols = mapOf( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 45ea108242..d8c8bf9dc2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -8,19 +8,24 @@ package software.amazon.smithy.rust.codegen.client.smithy.protocols import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.SensitiveIndex import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolParserGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -28,12 +33,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctio import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember import software.amazon.smithy.rust.codegen.core.util.outputShape -// TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` +// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up `enableNewSmithyRuntime` (replace with ClientProtocolGenerator) class HttpBoundProtocolGenerator( codegenContext: ClientCodegenContext, protocol: Protocol, - bodyGenerator: ProtocolPayloadGenerator = HttpBoundProtocolPayloadGenerator(codegenContext, protocol), -) : ClientProtocolGenerator( + bodyGenerator: ProtocolPayloadGenerator = ClientHttpBoundProtocolPayloadGenerator(codegenContext, protocol), +) : OperationGenerator( codegenContext, protocol, MakeOperationGenerator( @@ -47,7 +52,49 @@ class HttpBoundProtocolGenerator( HttpBoundProtocolTraitImplGenerator(codegenContext, protocol), ) -// TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` +// TODO(enableNewSmithyRuntimeCleanup): Completely delete `AdditionalPayloadContext` when switching to the orchestrator +data class ClientAdditionalPayloadContext( + val propertyBagAvailable: Boolean, +) : AdditionalPayloadContext + +class ClientHttpBoundProtocolPayloadGenerator( + codegenContext: ClientCodegenContext, + protocol: Protocol, +) : ProtocolPayloadGenerator by HttpBoundProtocolPayloadGenerator( + codegenContext, protocol, HttpMessageType.REQUEST, + renderEventStreamBody = { writer, params -> + val propertyBagAvailable = (params.additionalPayloadContext as ClientAdditionalPayloadContext).propertyBagAvailable + writer.rustTemplate( + """ + { + let error_marshaller = #{errorMarshallerConstructorFn}(); + let marshaller = #{marshallerConstructorFn}(); + let (signer, signer_sender) = #{DeferredSigner}::new(); + #{insert_into_config} + let adapter: #{aws_smithy_http}::event_stream::MessageStreamAdapter<_, _> = + ${params.outerName}.${params.memberName}.into_body_stream(marshaller, error_marshaller, signer); + let body: #{SdkBody} = #{hyper}::Body::wrap_stream(adapter).into(); + body + } + """, + "hyper" to CargoDependency.HyperWithStream.toType(), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "aws_smithy_http" to RuntimeType.smithyHttp(codegenContext.runtimeConfig), + "DeferredSigner" to RuntimeType.smithyEventStream(codegenContext.runtimeConfig).resolve("frame::DeferredSigner"), + "marshallerConstructorFn" to params.marshallerConstructorFn, + "errorMarshallerConstructorFn" to params.errorMarshallerConstructorFn, + "insert_into_config" to writable { + if (propertyBagAvailable) { + rust("properties.acquire_mut().insert(signer_sender);") + } else { + rust("_cfg.interceptor_state().store_put(signer_sender);") + } + }, + ) + }, +) + +// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up `enableNewSmithyRuntime` open class HttpBoundProtocolTraitImplGenerator( codegenContext: ClientCodegenContext, protocol: Protocol, @@ -60,6 +107,7 @@ open class HttpBoundProtocolTraitImplGenerator( private val parserGenerator = ProtocolParserGenerator(codegenContext, protocol) private val codegenScope = arrayOf( + *preludeScope, "ParseStrict" to RuntimeType.parseStrictResponse(runtimeConfig), "ParseResponse" to RuntimeType.parseHttpResponse(runtimeConfig), "http" to RuntimeType.Http, @@ -68,6 +116,8 @@ open class HttpBoundProtocolTraitImplGenerator( "SdkBody" to RuntimeType.sdkBody(runtimeConfig), ) + private val sensitiveIndex = SensitiveIndex.of(model) + open fun generateTraitImpls( operationWriter: RustWriter, operationShape: OperationShape, @@ -98,15 +148,20 @@ open class HttpBoundProtocolTraitImplGenerator( "O" to outputSymbol, "E" to symbolProvider.symbolForOperationError(operationShape), "parse_error" to parserGenerator.parseErrorFn(operationShape, customizations), - "parse_response" to parserGenerator.parseResponseFn(operationShape, customizations), + "parse_response" to parserGenerator.parseResponseFn(operationShape, true, customizations), "BeforeParseResponse" to writable { writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) }, ) + val sensitive = writable { + if (sensitiveIndex.hasSensitiveOutput(operationShape)) { + rust("fn sensitive(&self) -> bool { true }") + } + } rustTemplate( """ impl #{ParseStrict} for $operationName { - type Output = std::result::Result<#{O}, #{E}>; + type Output = #{Result}<#{O}, #{E}>; fn parse(&self, response: &#{http}::Response<#{Bytes}>) -> Self::Output { let (success, status) = (response.status().is_success(), response.status().as_u16()); let headers = response.headers(); @@ -118,9 +173,11 @@ open class HttpBoundProtocolTraitImplGenerator( #{parse_response}(status, headers, body) } } + #{sensitive} }""", *codegenScope, *localScope, + "sensitive" to sensitive, ) } @@ -134,14 +191,14 @@ open class HttpBoundProtocolTraitImplGenerator( rustTemplate( """ impl #{ParseResponse} for $operationName { - type Output = std::result::Result<#{O}, #{E}>; - fn parse_unloaded(&self, response: &mut #{operation}::Response) -> Option { + type Output = #{Result}<#{O}, #{E}>; + fn parse_unloaded(&self, response: &mut #{operation}::Response) -> #{Option} { #{BeforeParseResponse} // This is an error, defer to the non-streaming parser if !response.http().status().is_success() && response.http().status().as_u16() != $successCode { - return None; + return #{None}; } - Some(#{parse_streaming_response}(response)) + #{Some}(#{parse_streaming_response}(response)) } fn parse_loaded(&self, response: &#{http}::Response<#{Bytes}>) -> Self::Output { // if streaming, we only hit this case if its an error @@ -160,14 +217,17 @@ open class HttpBoundProtocolTraitImplGenerator( ) } - private fun parseStreamingResponse(operationShape: OperationShape, customizations: List): RuntimeType { + private fun parseStreamingResponse( + operationShape: OperationShape, + customizations: List, + ): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) val errorSymbol = symbolProvider.symbolForOperationError(operationShape) return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "op_response") { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) rustBlockTemplate( - "pub fn $fnName(op_response: &mut #{operation}::Response) -> std::result::Result<#{O}, #{E}>", + "pub fn $fnName(op_response: &mut #{operation}::Response) -> #{Result}<#{O}, #{E}>", *codegenScope, "O" to outputSymbol, "E" to errorSymbol, @@ -179,7 +239,11 @@ open class HttpBoundProtocolTraitImplGenerator( """ #{parse_streaming_response}(response, &properties) """, - "parse_streaming_response" to parserGenerator.parseStreamingResponseFn(operationShape, true, customizations), + "parse_streaming_response" to parserGenerator.parseStreamingResponseFn( + operationShape, + true, + customizations, + ), ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt index cb06688225..dbfa3b3c6c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt @@ -5,50 +5,102 @@ package software.amazon.smithy.rust.codegen.client.testutil +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfigGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.util.toPascalCase /** * Test helper to produce a valid config customization to test that a [ConfigCustomization] can be used in conjunction * with other [ConfigCustomization]s. */ -fun stubConfigCustomization(name: String): ConfigCustomization { +fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): ConfigCustomization { return object : ConfigCustomization() { override fun section(section: ServiceConfig): Writable = writable { when (section) { - ServiceConfig.ConfigStruct -> rust("_$name: u64,") - ServiceConfig.ConfigImpl -> rust( - """ - ##[allow(missing_docs)] - pub fn $name(&self) -> u64 { - self._$name + ServiceConfig.ConfigStruct -> { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rust("_$name: u64,") } - """, - ) - ServiceConfig.BuilderStruct -> rust("_$name: Option,") - ServiceConfig.BuilderImpl -> rust( - """ - /// docs! - pub fn $name(mut self, $name: u64) -> Self { - self._$name = Some($name); - self + } + ServiceConfig.ConfigImpl -> { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + """ + ##[allow(missing_docs)] + pub fn $name(&self) -> u64 { + self.config.load::<#{T}>().map(|u| u.0).unwrap() + } + """, + "T" to configParamNewtype( + "_$name".toPascalCase(), RuntimeType.U64.toSymbol(), + codegenContext.runtimeConfig, + ), + ) + } else { + rust( + """ + ##[allow(missing_docs)] + pub fn $name(&self) -> u64 { + self._$name + } + """, + ) } - """, - ) - ServiceConfig.BuilderBuild -> rust( - """ - _$name: self._$name.unwrap_or(123), - """, - ) + } + ServiceConfig.BuilderStruct -> { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rust("_$name: Option,") + } + } + ServiceConfig.BuilderImpl -> { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + """ + /// docs! + pub fn $name(mut self, $name: u64) -> Self { + self.config.store_put(#{T}($name)); + self + } + """, + "T" to configParamNewtype( + "_$name".toPascalCase(), RuntimeType.U64.toSymbol(), + codegenContext.runtimeConfig, + ), + ) + } else { + rust( + """ + /// docs! + pub fn $name(mut self, $name: u64) -> Self { + self._$name = Some($name); + self + } + """, + ) + } + } + ServiceConfig.BuilderBuild -> { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rust( + """ + _$name: self._$name.unwrap_or(123), + """, + ) + } + } else -> emptySection } } @@ -62,19 +114,20 @@ fun stubConfigCustomization(name: String): ConfigCustomization { * */ @Suppress("NAME_SHADOWING") fun validateConfigCustomizations( + codegenContext: ClientCodegenContext, customization: ConfigCustomization, project: TestWriterDelegator? = null, ): TestWriterDelegator { val project = project ?: TestWorkspace.testProject() - stubConfigProject(customization, project) + stubConfigProject(codegenContext, customization, project) project.compileAndTest() return project } -fun stubConfigProject(customization: ConfigCustomization, project: TestWriterDelegator): TestWriterDelegator { - val customizations = listOf(stubConfigCustomization("a")) + customization + stubConfigCustomization("b") - val generator = ServiceConfigGenerator(customizations = customizations.toList()) - project.withModule(ClientRustModule.Config) { +fun stubConfigProject(codegenContext: ClientCodegenContext, customization: ConfigCustomization, project: TestWriterDelegator): TestWriterDelegator { + val customizations = listOf(stubConfigCustomization("a", codegenContext)) + customization + stubConfigCustomization("b", codegenContext) + val generator = ServiceConfigGenerator(codegenContext, customizations = customizations.toList()) + project.withModule(ClientRustModule.config) { generator.render(this) unitTest( "config_send_sync", diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt index 1d2384b4a0..866cd7461f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientModuleProvider import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig @@ -23,6 +24,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig import software.amazon.smithy.rust.codegen.core.testutil.TestModuleDocProvider import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel fun testClientRustSettings( service: ShapeId = ShapeId.from("notrelevant#notrelevant"), @@ -72,7 +74,7 @@ fun testSymbolProvider(model: Model, serviceShape: ServiceShape? = null): RustSy ) fun testClientCodegenContext( - model: Model, + model: Model = "namespace empty".asSmithyModel(), symbolProvider: RustSymbolProvider? = null, serviceShape: ServiceShape? = null, settings: ClientRustSettings = testClientRustSettings(), @@ -89,6 +91,12 @@ fun testClientCodegenContext( rootDecorator ?: CombinedClientCodegenDecorator(emptyList()), ) +fun ClientCodegenContext.withSmithyRuntimeMode(smithyRuntimeMode: SmithyRuntimeMode): ClientCodegenContext = + copy(settings = settings.copy(codegenConfig = settings.codegenConfig.copy(enableNewSmithyRuntime = smithyRuntimeMode))) + +fun ClientCodegenContext.withEnableUserConfigurableRuntimePlugins(enableUserConfigurableRuntimePlugins: Boolean): ClientCodegenContext = + copy(settings = settings.copy(codegenConfig = settings.codegenConfig.copy(enableUserConfigurableRuntimePlugins = enableUserConfigurableRuntimePlugins))) + fun TestWriterDelegator.clientRustSettings() = testClientRustSettings( service = ShapeId.from("fake#Fake"), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt deleted file mode 100644 index 8c3a4122e9..0000000000 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.client.endpoint - -import org.junit.jupiter.api.Test -import software.amazon.smithy.rust.codegen.client.smithy.endpoint.ClientContextConfigCustomization -import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext -import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations -import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace -import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.core.testutil.unitTest - -class ClientContextConfigCustomizationTest { - val model = """ - namespace test - use smithy.rules#clientContextParams - - @clientContextParams(aStringParam: { - documentation: "string docs", - type: "string" - }, - aBoolParam: { - documentation: "bool docs", - type: "boolean" - }) - service TestService { operations: [] } - """.asSmithyModel() - - @Test - fun `client params generate a valid customization`() { - val project = TestWorkspace.testProject() - project.unitTest { - rust( - """ - let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); - assert_eq!(conf.a_string_param.unwrap(), "hello!"); - assert_eq!(conf.a_bool_param, Some(true)); - """, - ) - } - // unset fields - project.unitTest { - rust( - """ - let conf = crate::Config::builder().a_string_param("hello!").build(); - assert_eq!(conf.a_string_param.unwrap(), "hello!"); - assert_eq!(conf.a_bool_param, None); - """, - ) - } - validateConfigCustomizations(ClientContextConfigCustomization(testClientCodegenContext(model)), project) - } -} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitorTest.kt index 2cb21423fd..f9cf52375b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitorTest.kt @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientDecorator import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel @@ -52,7 +51,6 @@ class ClientCodegenVisitorTest { ClientCustomizations(), RequiredCustomizations(), FluentClientDecorator(), - NoOpEventStreamSigningDecorator(), ) val visitor = ClientCodegenVisitor(ctx, codegenDecorator) val baselineModel = visitor.baselineTransform(model) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/EventStreamSymbolProviderTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/EventStreamSymbolProviderTest.kt index 703ea2a7af..e9e26724d9 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/EventStreamSymbolProviderTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/EventStreamSymbolProviderTest.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSetting import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.EventStreamSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer @@ -60,8 +61,17 @@ class EventStreamSymbolProviderTest { val inputType = provider.toSymbol(inputStream).rustType() val outputType = provider.toSymbol(outputStream).rustType() - inputType shouldBe RustType.Opaque("EventStreamSender", "aws_smithy_http::event_stream") - outputType shouldBe RustType.Opaque("Receiver", "aws_smithy_http::event_stream") + val someStream = RustType.Opaque("SomeStream", "crate::types") + val someStreamError = RustType.Opaque("SomeStreamError", "crate::types::error") + + inputType shouldBe RustType.Application( + RuntimeType.eventStreamSender(TestRuntimeConfig).toSymbol().rustType(), + listOf(someStream, someStreamError), + ) + outputType shouldBe RustType.Application( + RuntimeType.eventStreamReceiver(TestRuntimeConfig).toSymbol().rustType(), + listOf(someStream, someStreamError), + ) } @Test diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt new file mode 100644 index 0000000000..aba0edc1a5 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeEnumVariantsModel +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeEnumsModel +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeOperationsModel +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeStructsModel + +class NamingObstacleCourseTest { + @Test + fun `test Rust prelude operation names compile`() { + clientIntegrationTest(rustPreludeOperationsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude structure names compile`() { + clientIntegrationTest(rustPreludeStructsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude enum names compile`() { + clientIntegrationTest(rustPreludeEnumsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude enum variant names compile`() { + clientIntegrationTest(rustPreludeEnumVariantsModel()) { _, _ -> } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/StreamingShapeSymbolProviderTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/StreamingShapeSymbolProviderTest.kt index a2e233c719..61d97317f2 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/StreamingShapeSymbolProviderTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/StreamingShapeSymbolProviderTest.kt @@ -43,13 +43,13 @@ internal class StreamingShapeSymbolProviderTest { modelWithOperationTraits.lookup("test.synthetic#GenerateSpeechOutput\$data").also { shape -> symbolProvider.toSymbol(shape).also { symbol -> symbol.name shouldBe "data" - symbol.rustType() shouldBe RustType.Opaque("ByteStream", "aws_smithy_http::byte_stream") + symbol.rustType() shouldBe RustType.Opaque("ByteStream", "::aws_smithy_http::byte_stream") } } modelWithOperationTraits.lookup("test.synthetic#GenerateSpeechInput\$data").also { shape -> symbolProvider.toSymbol(shape).also { symbol -> symbol.name shouldBe "data" - symbol.rustType() shouldBe RustType.Opaque("ByteStream", "aws_smithy_http::byte_stream") + symbol.rustType() shouldBe RustType.Opaque("ByteStream", "::aws_smithy_http::byte_stream") } } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ApiKeyAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt similarity index 92% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ApiKeyAuthDecoratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt index b44e427777..a6da0a29a3 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ApiKeyAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt @@ -3,17 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.customizations +package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest import software.amazon.smithy.rust.codegen.core.testutil.runWithWarnings +// TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced by HttpAuthDecoratorTest) internal class ApiKeyAuthDecoratorTest { private val modelQuery = """ namespace test @@ -46,7 +47,8 @@ internal class ApiKeyAuthDecoratorTest { val testDir = clientIntegrationTest( modelQuery, // just run integration tests - IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + TestCodegenSettings.middlewareModeTestParams + .copy(command = { "cargo test --test *".runWithWarnings(it) }), ) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("api_key_present_in_property_bag") { val moduleName = clientCodegenContext.moduleUseName() @@ -136,7 +138,8 @@ internal class ApiKeyAuthDecoratorTest { val testDir = clientIntegrationTest( modelHeader, // just run integration tests - IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + TestCodegenSettings.middlewareModeTestParams + .copy(command = { "cargo test --test *".runWithWarnings(it) }), ) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("api_key_auth_is_set_in_http_header") { val moduleName = clientCodegenContext.moduleUseName() diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt new file mode 100644 index 0000000000..29f25e98af --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -0,0 +1,492 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.integrationTest + +class HttpAuthDecoratorTest { + private fun codegenScope(runtimeConfig: RuntimeConfig): Array> = arrayOf( + "TestConnection" to CargoDependency.smithyClient(runtimeConfig) + .toDevDependency().withFeature("test-util").toType() + .resolve("test_connection::TestConnection"), + "SdkBody" to RuntimeType.sdkBody(runtimeConfig), + ) + + @Test + fun multipleAuthSchemesSchemeSelection() { + clientIntegrationTest( + TestModels.allSchemes, + TestCodegenSettings.orchestratorModeTestParams, + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("tests") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn use_api_key_auth_when_api_key_provided() { + use aws_smithy_runtime_api::client::identity::http::Token; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .uri("http://localhost:1234/SomeOperation?api_key=some-api-key") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .api_key(Token::new("some-api-key", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn use_basic_auth_when_basic_auth_login_provided() { + use aws_smithy_runtime_api::client::identity::http::Login; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .header("authorization", "Basic c29tZS11c2VyOnNvbWUtcGFzcw==") + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .basic_auth_login(Login::new("some-user", "some-pass", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun apiKeyInQueryString() { + clientIntegrationTest( + TestModels.apiKeyInQueryString, + TestCodegenSettings.orchestratorModeTestParams, + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("api_key_applied_to_query_string") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn api_key_applied_to_query_string() { + use aws_smithy_runtime_api::client::identity::http::Token; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .uri("http://localhost:1234/SomeOperation?api_key=some-api-key") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .api_key(Token::new("some-api-key", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun apiKeyInHeaders() { + clientIntegrationTest( + TestModels.apiKeyInHeaders, + TestCodegenSettings.orchestratorModeTestParams, + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("api_key_applied_to_headers") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn api_key_applied_to_headers() { + use aws_smithy_runtime_api::client::identity::http::Token; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .header("authorization", "ApiKey some-api-key") + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .api_key(Token::new("some-api-key", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun basicAuth() { + clientIntegrationTest( + TestModels.basicAuth, + TestCodegenSettings.orchestratorModeTestParams, + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("basic_auth") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn basic_auth() { + use aws_smithy_runtime_api::client::identity::http::Login; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .header("authorization", "Basic c29tZS11c2VyOnNvbWUtcGFzcw==") + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .basic_auth_login(Login::new("some-user", "some-pass", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun bearerAuth() { + clientIntegrationTest( + TestModels.bearerAuth, + TestCodegenSettings.orchestratorModeTestParams, + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("bearer_auth") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn basic_auth() { + use aws_smithy_runtime_api::client::identity::http::Token; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .header("authorization", "Bearer some-token") + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .bearer_token(Token::new("some-token", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun optionalAuth() { + clientIntegrationTest( + TestModels.optionalAuth, + TestCodegenSettings.orchestratorModeTestParams, + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("optional_auth") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn optional_auth() { + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } +} + +private object TestModels { + val allSchemes = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpApiKeyAuth(name: "api_key", in: "query") + @httpBasicAuth + @httpBearerAuth + @httpDigestAuth + @auth([httpApiKeyAuth, httpBasicAuth, httpBearerAuth, httpDigestAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val apiKeyInQueryString = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpApiKeyAuth(name: "api_key", in: "query") + @auth([httpApiKeyAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val apiKeyInHeaders = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpApiKeyAuth(name: "authorization", in: "header", scheme: "ApiKey") + @auth([httpApiKeyAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val basicAuth = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpBasicAuth + @auth([httpBasicAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val bearerAuth = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpBearerAuth + @auth([httpBearerAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val optionalAuth = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpBearerAuth + @auth([httpBearerAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + @optionalAuth + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/HttpVersionListGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt similarity index 62% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/HttpVersionListGeneratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt index 7536629578..602e848261 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/HttpVersionListGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt @@ -3,23 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.customizations +package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.EventStreamSigningConfig -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest @@ -28,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.integrationTest // ./gradlew codegen-client:test --tests software.amazon.smithy.rust.codegen.client.HttpVersionListGeneratorTest --info // ``` +// TODO(enableNewSmithyRuntimeCleanup): Delete this test (incomplete http version support wasn't ported to orchestrator) internal class HttpVersionListGeneratorTest { @Test fun `http version list integration test (no preferred version)`() { @@ -54,7 +45,7 @@ internal class HttpVersionListGeneratorTest { greeting: String } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("http_version_list") { Attribute.TokioTest.render(this) @@ -105,7 +96,7 @@ internal class HttpVersionListGeneratorTest { greeting: String } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("validate_http") { Attribute.TokioTest.render(this) @@ -171,8 +162,7 @@ internal class HttpVersionListGeneratorTest { clientIntegrationTest( model, - IntegrationTestParams(addModuleToEventStreamAllowList = true), - additionalDecorators = listOf(FakeSigningDecorator()), + TestCodegenSettings.middlewareModeTestParams.copy(addModuleToEventStreamAllowList = true), ) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("validate_eventstream_http") { @@ -196,77 +186,3 @@ internal class HttpVersionListGeneratorTest { } } } - -class FakeSigningDecorator : ClientCodegenDecorator { - override val name: String = "fakesigning" - override val order: Byte = 0 - override fun classpathDiscoverable(): Boolean = false - override fun configCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - return baseCustomizations.filterNot { - it is EventStreamSigningConfig - } + FakeSigningConfig(codegenContext.runtimeConfig) - } -} - -class FakeSigningConfig( - runtimeConfig: RuntimeConfig, -) : EventStreamSigningConfig(runtimeConfig) { - private val codegenScope = arrayOf( - "SharedPropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::SharedPropertyBag"), - "SignMessageError" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::SignMessageError"), - "SignMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::SignMessage"), - "Message" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::Message"), - ) - - override fun section(section: ServiceConfig): Writable { - return when (section) { - is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Creates a new Event Stream `SignMessage` implementor. - pub fn new_event_stream_signer( - &self, - properties: #{SharedPropertyBag} - ) -> FakeSigner { - FakeSigner::new(properties) - } - """, - *codegenScope, - ) - } - - is ServiceConfig.Extras -> writable { - rustTemplate( - """ - /// Fake signing implementation. - ##[derive(Debug)] - pub struct FakeSigner; - - impl FakeSigner { - /// Create a real `FakeSigner` - pub fn new(_properties: #{SharedPropertyBag}) -> Self { - Self {} - } - } - - impl #{SignMessage} for FakeSigner { - fn sign(&mut self, message: #{Message}) -> Result<#{Message}, #{SignMessageError}> { - Ok(message) - } - - fn sign_empty(&mut self) -> Option> { - Some(Ok(#{Message}::new(Vec::new()))) - } - } - """, - *codegenScope, - ) - } - - else -> emptySection - } - } -} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt new file mode 100644 index 0000000000..3463ab1e73 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import software.amazon.smithy.rust.codegen.core.testutil.tokioTest + +class MetadataCustomizationTest { + private val model = """ + namespace com.example + use aws.protocols#awsJson1_0 + @awsJson1_0 + service HelloService { + operations: [SayHello], + version: "1" + } + @optionalAuth + operation SayHello { input: TestInput } + structure TestInput { + foo: String, + } + """.asSmithyModel() + + @Test + fun `extract metadata via customizable operation`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *preludeScope, + "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig), + "BoxError" to RuntimeType.boxError(runtimeConfig), + "ConfigBag" to RuntimeType.configBag(runtimeConfig), + "Interceptor" to RuntimeType.interceptor(runtimeConfig), + "Metadata" to RuntimeType.operationModule(runtimeConfig).resolve("Metadata"), + "capture_request" to RuntimeType.captureRequest(runtimeConfig), + "RuntimeComponents" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_components::RuntimeComponents"), + ) + rustCrate.testModule { + addDependency(CargoDependency.Tokio.toDevDependency().withFeature("test-util")) + tokioTest("test_extract_metadata_via_customizable_operation") { + rustTemplate( + """ + // Interceptors aren’t supposed to store states, but it is done this way for a testing purpose. + ##[derive(Debug)] + struct ExtractMetadataInterceptor( + ::std::sync::Mutex<#{Option}<::std::sync::mpsc::Sender<(String, String)>>>, + ); + + impl #{Interceptor} for ExtractMetadataInterceptor { + fn name(&self) -> &'static str { + "ExtractMetadataInterceptor" + } + + fn modify_before_signing( + &self, + _context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, + _runtime_components: &#{RuntimeComponents}, + cfg: &mut #{ConfigBag}, + ) -> #{Result}<(), #{BoxError}> { + let metadata = cfg + .load::<#{Metadata}>() + .expect("metadata should exist"); + let service_name = metadata.service().to_string(); + let operation_name = metadata.name().to_string(); + let tx = self.0.lock().unwrap().take().unwrap(); + tx.send((service_name, operation_name)).unwrap(); + #{Ok}(()) + } + } + + let (tx, rx) = ::std::sync::mpsc::channel(); + + let (conn, _captured_request) = #{capture_request}(#{None}); + let client_config = crate::config::Config::builder() + .endpoint_resolver("http://localhost:1234/") + .http_connector(conn) + .build(); + let client = crate::client::Client::from_conf(client_config); + let _ = client + .say_hello() + .customize() + .await + .expect("operation should be customizable") + .interceptor(ExtractMetadataInterceptor(::std::sync::Mutex::new(#{Some}(tx)))) + .send() + .await; + + match rx.recv() { + #{Ok}((service_name, operation_name)) => { + assert_eq!("HelloService", &service_name); + assert_eq!("SayHello", &operation_name); + } + #{Err}(_) => panic!( + "failed to receive service name and operation name from `ExtractMetadataInterceptor`" + ), + } + """, + *codegenScope, + ) + } + } + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ResiliencyConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt similarity index 70% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ResiliencyConfigCustomizationTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt index 612bf8e333..b25e28c75e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ResiliencyConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.customizations +package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenConfig -import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginGenerator import software.amazon.smithy.rust.codegen.client.testutil.clientRustSettings import software.amazon.smithy.rust.codegen.client.testutil.stubConfigProject import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext @@ -41,8 +41,14 @@ internal class ResiliencyConfigCustomizationTest { val project = TestWorkspace.testProject(model, ClientCodegenConfig()) val codegenContext = testClientCodegenContext(model, settings = project.clientRustSettings()) - stubConfigProject(ResiliencyConfigCustomization(codegenContext), project) - ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(project) + stubConfigProject(codegenContext, ResiliencyConfigCustomization(codegenContext), project) + project.withModule(ClientRustModule.config) { + ServiceRuntimePluginGenerator(codegenContext).render( + this, + listOf(ResiliencyServiceRuntimePluginCustomization(codegenContext)), + ) + } + ResiliencyReExportCustomization(codegenContext).extras(project) project.compileAndTest() } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt new file mode 100644 index 0000000000..b855c31366 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.endpoint + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext +import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.unitTest + +class ClientContextConfigCustomizationTest { + val model = """ + namespace test + use smithy.rules#clientContextParams + + @clientContextParams(aStringParam: { + documentation: "string docs", + type: "string" + }, + aBoolParam: { + documentation: "bool docs", + type: "boolean" + }) + service TestService { operations: [] } + """.asSmithyModel() + + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `client params generate a valid customization`(smithyRuntimeModeStr: String) { + val project = TestWorkspace.testProject() + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val context = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) + project.unitTest { + if (smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + """ + use #{RuntimePlugin}; + let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); + assert_eq!( + conf.config + .load::() + .map(|u| u.0.clone()) + .unwrap(), + "hello!" + ); + assert_eq!( + conf.config + .load::() + .map(|u| u.0), + Some(true) + ); + """, + "RuntimePlugin" to RuntimeType.runtimePlugin(context.runtimeConfig), + ) + } else { + rust( + """ + let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); + assert_eq!(conf.a_string_param.unwrap(), "hello!"); + assert_eq!(conf.a_bool_param, Some(true)); + """, + ) + } + } + // unset fields + project.unitTest { + if (smithyRuntimeMode.generateOrchestrator) { + rustTemplate( + """ + use #{RuntimePlugin}; + let conf = crate::Config::builder().a_string_param("hello!").build(); + assert_eq!( + conf.config + .load::() + .map(|u| u.0.clone()) + .unwrap(), + "hello!" + ); + assert_eq!( + conf.config + .load::() + .map(|u| u.0), + None, + ); + """, + "RuntimePlugin" to RuntimeType.runtimePlugin(context.runtimeConfig), + ) + } else { + rust( + """ + let conf = crate::Config::builder().a_string_param("hello!").build(); + assert_eq!(conf.a_string_param.unwrap(), "hello!"); + assert_eq!(conf.a_bool_param, None); + """, + ) + } + } + validateConfigCustomizations(context, ClientContextConfigCustomization(context), project) + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointParamsGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt similarity index 82% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointParamsGeneratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt index fedc249794..34636f596c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointParamsGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.endpoint +package software.amazon.smithy.rust.codegen.client.smithy.endpoint import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import software.amazon.smithy.rulesengine.testutil.TestDiscovery import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsGenerator +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest @@ -25,6 +26,7 @@ internal class EndpointParamsGeneratorTest { @MethodSource("testSuites") fun `generate endpoint params for provided test suites`(testSuite: TestDiscovery.RulesTestSuite) { val project = TestWorkspace.testProject() + val context = testClientCodegenContext() project.lib { unitTest("params_work") { rustTemplate( @@ -32,7 +34,7 @@ internal class EndpointParamsGeneratorTest { // this might fail if there are required fields let _ = #{Params}::builder().build(); """, - "Params" to EndpointParamsGenerator(testSuite.ruleSet().parameters).paramsStruct(), + "Params" to EndpointParamsGenerator(context, testSuite.ruleSet().parameters).paramsStruct(), ) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointResolverGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt similarity index 91% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointResolverGeneratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt index 179cbbc944..5a798cae4d 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointResolverGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.endpoint +package software.amazon.smithy.rust.codegen.client.smithy.endpoint import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test @@ -53,15 +53,16 @@ class EndpointResolverGeneratorTest { // return } val project = TestWorkspace.testProject() + val context = testClientCodegenContext() suite.ruleSet().typecheck() project.lib { val ruleset = EndpointResolverGenerator( + context, SmithyEndpointsStdLib + awsStandardLib(TestRuntimeConfig, partitionsJson), - TestRuntimeConfig, ).defaultEndpointResolver(suite.ruleSet()) val testGenerator = EndpointTestGenerator( suite.testSuite().testCases, - paramsType = EndpointParamsGenerator(suite.ruleSet().parameters).paramsStruct(), + paramsType = EndpointParamsGenerator(context, suite.ruleSet().parameters).paramsStruct(), resolverType = ruleset, suite.ruleSet().parameters, codegenContext = testClientCodegenContext(model = Model.builder().build()), @@ -79,15 +80,16 @@ class EndpointResolverGeneratorTest { testSuites().filter { it.ruleSet().sourceLocation.filename.endsWith("/uri-encode.json") }.findFirst() .orElseThrow() val project = TestWorkspace.testProject() + val context = testClientCodegenContext() suite.ruleSet().typecheck() project.lib { val ruleset = EndpointResolverGenerator( + context, SmithyEndpointsStdLib + awsStandardLib(TestRuntimeConfig, partitionsJson), - TestRuntimeConfig, ).defaultEndpointResolver(suite.ruleSet()) val testGenerator = EndpointTestGenerator( suite.testSuite().testCases, - paramsType = EndpointParamsGenerator(suite.ruleSet().parameters).paramsStruct(), + paramsType = EndpointParamsGenerator(context, suite.ruleSet().parameters).paramsStruct(), resolverType = ruleset, suite.ruleSet().parameters, codegenContext = testClientCodegenContext(Model.builder().build()), @@ -115,7 +117,8 @@ class EndpointResolverGeneratorTest { val scope = Scope() scope.insert("Region", Type.string()) endpoint.typeCheck(scope) - val generator = EndpointResolverGenerator(listOf(), TestRuntimeConfig) + val context = testClientCodegenContext() + val generator = EndpointResolverGenerator(context, listOf()) TestWorkspace.testProject().unitTest { rustTemplate( """ diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointsDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt similarity index 54% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointsDecoratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt index fc564c2696..9d2e74c1b7 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointsDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt @@ -3,11 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.endpoint +package software.amazon.smithy.rust.codegen.client.smithy.endpoint import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.string.shouldContain import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -15,7 +16,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest import software.amazon.smithy.rust.codegen.core.testutil.runWithWarnings -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError /** * End-to-end test of endpoint resolvers, attaching a real resolver to a fully generated service @@ -120,12 +121,14 @@ class EndpointsDecoratorTest { } """.asSmithyModel() + // TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced by the second @Test below) @Test fun `set an endpoint in the property bag`() { val testDir = clientIntegrationTest( model, // Just run integration tests. - IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + TestCodegenSettings.middlewareModeTestParams + .copy(command = { "cargo test --test *".runWithWarnings(it) }), ) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("endpoint_params_test") { val moduleName = clientCodegenContext.moduleUseName() @@ -162,7 +165,109 @@ class EndpointsDecoratorTest { } } // the model has an intentionally failing test—ensure it fails - val failure = shouldThrow { "cargo test".runWithWarnings(testDir) } + val failure = shouldThrow { "cargo test".runWithWarnings(testDir) } + failure.output shouldContain "endpoint::test::test_1" + failure.output shouldContain "https://failingtest.com" + "cargo clippy".runWithWarnings(testDir) + } + + @Test + fun `resolve endpoint`() { + val testDir = clientIntegrationTest( + model, + // Just run integration tests. + IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + ) { clientCodegenContext, rustCrate -> + rustCrate.integrationTest("endpoint_params_test") { + val moduleName = clientCodegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rust( + """ + async fn endpoint_params_are_set() { + use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_client::never::NeverConnector; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::endpoint::EndpointResolverParams; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::endpoint::Endpoint; + use aws_smithy_types::timeout::TimeoutConfig; + use std::sync::atomic::AtomicBool; + use std::sync::atomic::Ordering; + use std::sync::Arc; + use std::time::Duration; + use $moduleName::{ + config::endpoint::Params, config::interceptors::BeforeTransmitInterceptorContextRef, + config::Interceptor, config::SharedAsyncSleep, Client, Config, + }; + + ##[derive(Clone, Debug, Default)] + struct TestInterceptor { + called: Arc, + } + impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + + fn read_before_transmit( + &self, + _context: &BeforeTransmitInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let params = cfg + .load::() + .expect("params set in config"); + let params: &Params = params.get().expect("correct type"); + assert_eq!( + params, + &Params::builder() + .bucket("bucket-name".to_string()) + .built_in_with_default("some-default") + .bool_built_in_with_default(true) + .a_bool_param(false) + .a_string_param("hello".to_string()) + .region("us-east-2".to_string()) + .build() + .unwrap() + ); + + let endpoint = cfg.load::().expect("endpoint set in config"); + assert_eq!(endpoint.url(), "https://www.us-east-2.example.com"); + + self.called.store(true, Ordering::Relaxed); + Ok(()) + } + } + + let interceptor = TestInterceptor::default(); + let config = Config::builder() + .http_connector(NeverConnector::new()) + .interceptor(interceptor.clone()) + .timeout_config( + TimeoutConfig::builder() + .operation_timeout(Duration::from_millis(30)) + .build(), + ) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) + .a_string_param("hello") + .a_bool_param(false) + .build(); + let client = Client::from_conf(config); + + let _ = dbg!(client.test_operation().bucket("bucket-name").send().await); + assert!( + interceptor.called.load(Ordering::Relaxed), + "the interceptor should have been called" + ); + } + """, + ) + } + } + // the model has an intentionally failing test—ensure it fails + val failure = shouldThrow { "cargo test".runWithWarnings(testDir) } failure.output shouldContain "endpoint::test::test_1" failure.output shouldContain "https://failingtest.com" "cargo clippy".runWithWarnings(testDir) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGeneratorTest.kt index 34781b1eac..823d6ace78 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGeneratorTest.kt @@ -14,7 +14,6 @@ import software.amazon.smithy.rulesengine.language.syntax.Identifier import software.amazon.smithy.rulesengine.language.syntax.expr.Expression import software.amazon.smithy.rulesengine.language.syntax.expr.Literal import software.amazon.smithy.rulesengine.language.syntax.expr.Template -import software.amazon.smithy.rulesengine.language.syntax.fn.LibraryFunction import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Context import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.FunctionRegistry import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -26,25 +25,13 @@ import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest internal class ExprGeneratorTest { - /** - * This works around a bug in smithy-endpoint-rules where the constructors on functions like `BooleanEquals` - * hit the wrong branch in the visitor (but when they get parsed, they hit the right branch). - */ - fun Expression.shoop() = Expression.fromNode(this.toNode()) private val testContext = Context(FunctionRegistry(listOf()), TestRuntimeConfig) - @Test - fun `fail when smithy is fixed`() { - check(BooleanEquals.ofExpressions(Expression.of(true), Expression.of(true)) is LibraryFunction) { - "smithy has been fixed, shoop can be removed" - } - } - @Test fun generateExprs() { - val boolEq = Expression.of(true).equal(true).shoop() - val strEq = Expression.of("helloworld").equal("goodbyeworld").not().shoop() - val combined = BooleanEquals.ofExpressions(boolEq, strEq).shoop() + val boolEq = Expression.of(true).equal(true) + val strEq = Expression.of("helloworld").equal("goodbyeworld").not() + val combined = BooleanEquals.ofExpressions(boolEq, strEq) TestWorkspace.testProject().unitTest { val generator = ExpressionGenerator(Ownership.Borrowed, testContext) rust("assert_eq!(true, #W);", generator.generate(boolEq)) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt index abd497b49b..49905fdeb9 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt @@ -47,7 +47,7 @@ internal class ClientInstantiatorTest { @Test fun `generate named enums`() { val shape = model.lookup("com.test#NamedEnum") - val sut = clientInstantiator(codegenContext) + val sut = ClientInstantiator(codegenContext) val data = Node.parse("t2.nano".dq()) val project = TestWorkspace.testProject(symbolProvider) @@ -66,7 +66,7 @@ internal class ClientInstantiatorTest { @Test fun `generate unnamed enums`() { val shape = model.lookup("com.test#UnnamedEnum") - val sut = clientInstantiator(codegenContext) + val sut = ClientInstantiator(codegenContext) val data = Node.parse("t2.nano".dq()) val project = TestWorkspace.testProject(symbolProvider) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt new file mode 100644 index 0000000000..48821e380d --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt @@ -0,0 +1,265 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.smithyRuntimeApiTestUtil +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import software.amazon.smithy.rust.codegen.core.testutil.tokioTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest + +internal class ConfigOverrideRuntimePluginGeneratorTest { + private val model = """ + namespace com.example + use aws.protocols#awsJson1_0 + + @awsJson1_0 + service HelloService { + operations: [SayHello], + version: "1" + } + + @optionalAuth + operation SayHello { input: TestInput } + structure TestInput { + foo: String, + } + """.asSmithyModel() + + @Test + fun `operation overrides endpoint resolver`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *preludeScope, + "EndpointResolverParams" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::endpoint::EndpointResolverParams"), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + ) + rustCrate.testModule { + addDependency(CargoDependency.Tokio.toDevDependency().withFeature("test-util")) + tokioTest("test_operation_overrides_endpoint_resolver") { + rustTemplate( + """ + use #{RuntimePlugin}; + use ::aws_smithy_runtime_api::client::endpoint::EndpointResolver; + + let expected_url = "http://localhost:1234/"; + let client_config = crate::config::Config::builder().build(); + let config_override = + crate::config::Config::builder().endpoint_resolver(expected_url); + let sut = crate::config::ConfigOverrideRuntimePlugin::new( + config_override, + client_config.config, + &client_config.runtime_components, + ); + let sut_components = sut.runtime_components(); + let endpoint_resolver = sut_components.endpoint_resolver().unwrap(); + let endpoint = endpoint_resolver + .resolve_endpoint(&#{EndpointResolverParams}::new(crate::config::endpoint::Params {})) + .await + .unwrap(); + + assert_eq!(expected_url, endpoint.url()); + """, + *codegenScope, + ) + } + } + } + } + + @Test + fun `operation overrides http connector`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *preludeScope, + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + ) + rustCrate.testModule { + addDependency(CargoDependency.Tokio.toDevDependency().withFeature("test-util")) + tokioTest("test_operation_overrides_http_connection") { + rustTemplate( + """ + use #{AsyncSleep}; + + let (conn, captured_request) = #{capture_request}(#{None}); + let expected_url = "http://localhost:1234/"; + let client_config = crate::config::Config::builder() + .endpoint_resolver(expected_url) + .http_connector(#{NeverConnector}::new()) + .build(); + let client = crate::client::Client::from_conf(client_config.clone()); + let sleep = #{TokioSleep}::new(); + + let send = client.say_hello().send(); + let timeout = #{Timeout}::new( + send, + sleep.sleep(::std::time::Duration::from_millis(100)), + ); + + // sleep future should win because the other future `send` is backed by `NeverConnector`, + // yielding `Err` + matches!(timeout.await, Err(_)); + + let client = crate::client::Client::from_conf(client_config); + let customizable_send = client + .say_hello() + .customize() + .await + .unwrap() + .config_override(crate::config::Config::builder().http_connector(conn)) + .send(); + + let timeout = #{Timeout}::new( + customizable_send, + sleep.sleep(::std::time::Duration::from_millis(100)), + ); + + // `conn` should shadow `NeverConnector` by virtue of `config_override` + match timeout.await { + Ok(_) => { + assert_eq!( + expected_url, + captured_request.expect_request().uri().to_string() + ); + } + Err(_) => { + panic!("this arm should not be reached since the `customizable_send` future should win"); + } + } + """, + *codegenScope, + "AsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::AsyncSleep"), + "capture_request" to RuntimeType.captureRequest(runtimeConfig), + "NeverConnector" to RuntimeType.smithyClient(runtimeConfig) + .resolve("never::NeverConnector"), + "Timeout" to RuntimeType.smithyAsync(runtimeConfig).resolve("future::timeout::Timeout"), + "TokioSleep" to RuntimeType.smithyAsync(runtimeConfig) + .resolve("rt::sleep::TokioSleep"), + ) + } + } + } + } + + @Test + fun `operation overrides retry strategy`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *preludeScope, + "AlwaysRetry" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::retries::AlwaysRetry"), + "ConfigBag" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag"), + "ErrorKind" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::ErrorKind"), + "Input" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Input"), + "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), + "Layer" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Layer"), + "OrchestratorError" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::OrchestratorError"), + "RetryConfig" to RuntimeType.smithyTypes(clientCodegenContext.runtimeConfig) + .resolve("retry::RetryConfig"), + "RequestAttempts" to smithyRuntimeApiTestUtil(runtimeConfig).toType() + .resolve("client::retries::RequestAttempts"), + "RetryClassifiers" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::retries::RetryClassifiers"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "ShouldAttempt" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::retries::ShouldAttempt"), + ) + rustCrate.testModule { + unitTest("test_operation_overrides_retry_strategy") { + rustTemplate( + """ + use #{RuntimePlugin}; + use ::aws_smithy_runtime_api::client::retries::RetryStrategy; + + let client_config = crate::config::Config::builder() + .retry_config(#{RetryConfig}::standard().with_max_attempts(3)) + .build(); + + let mut ctx = #{InterceptorContext}::new(#{Input}::doesnt_matter()); + ctx.set_output_or_error(#{Err}(#{OrchestratorError}::other("doesn't matter"))); + + let mut layer = #{Layer}::new("test"); + layer.store_put(#{RequestAttempts}::new(1)); + + let mut cfg = #{ConfigBag}::of_layers(vec![layer]); + let client_config_layer = client_config.config; + cfg.push_shared_layer(client_config_layer.clone()); + + let retry_classifiers_component = #{RuntimeComponentsBuilder}::new("retry_classifier") + .with_retry_classifiers(#{Some}( + #{RetryClassifiers}::new().with_classifier(#{AlwaysRetry}(#{ErrorKind}::TransientError)), + )); + + // Emulate the merging of runtime components from runtime plugins that the orchestrator does + let runtime_components = #{RuntimeComponentsBuilder}::for_tests() + .merge_from(&client_config.runtime_components) + .merge_from(&retry_classifiers_component) + .build() + .unwrap(); + + let retry = runtime_components.retry_strategy(); + assert!(matches!( + retry.should_attempt_retry(&ctx, &runtime_components, &cfg).unwrap(), + #{ShouldAttempt}::YesAfterDelay(_) + )); + + // sets `max_attempts` to 1 implicitly by using `disabled()`, forcing it to run out of + // attempts with respect to `RequestAttempts` set to 1 above + let config_override_builder = crate::config::Config::builder() + .retry_config(#{RetryConfig}::disabled()); + let config_override = config_override_builder.clone().build(); + let sut = crate::config::ConfigOverrideRuntimePlugin::new( + config_override_builder, + client_config_layer, + &client_config.runtime_components, + ); + let sut_layer = sut.config().unwrap(); + cfg.push_shared_layer(sut_layer); + + // Emulate the merging of runtime components from runtime plugins that the orchestrator does + let runtime_components = #{RuntimeComponentsBuilder}::for_tests() + .merge_from(&client_config.runtime_components) + .merge_from(&retry_classifiers_component) + .merge_from(&config_override.runtime_components) + .build() + .unwrap(); + + let retry = runtime_components.retry_strategy(); + assert!(matches!( + retry.should_attempt_retry(&ctx, &runtime_components, &cfg).unwrap(), + #{ShouldAttempt}::No + )); + """, + *codegenScope, + ) + } + } + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt index 841986cb91..845501945b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt @@ -7,15 +7,21 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig @@ -34,8 +40,10 @@ internal class EndpointTraitBindingsTest { epTrait.prefixFormatString() shouldBe ("\"{foo}.data\"") } - @Test - fun `generate endpoint prefixes`() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generate endpoint prefixes`(smithyRuntimeModeStr: String) { + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) val model = """ namespace test @readonly @@ -73,7 +81,7 @@ internal class EndpointTraitBindingsTest { RuntimeType.smithyHttp(TestRuntimeConfig), TestRuntimeConfig.operationBuildError(), ) { - endpointBindingGenerator.render(this, "self") + endpointBindingGenerator.render(this, "self", smithyRuntimeMode) } } unitTest( @@ -115,9 +123,10 @@ internal class EndpointTraitBindingsTest { project.compileAndTest() } + // TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced by the @Test below it) @ExperimentalPathApi @Test - fun `endpoint integration test`() { + fun `endpoint integration test middleware`() { val model = """ namespace com.example use aws.protocols#awsJson1_0 @@ -137,7 +146,7 @@ internal class EndpointTraitBindingsTest { greeting: String } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("test_endpoint_prefix") { Attribute.TokioTest.render(this) @@ -163,4 +172,140 @@ internal class EndpointTraitBindingsTest { } } } + + @ExperimentalPathApi + @Test + fun `endpoint integration test`() { + val model = """ + namespace com.example + use aws.protocols#awsJson1_0 + use smithy.rules#endpointRuleSet + + @awsJson1_0 + @aws.api#service(sdkId: "Test", endpointPrefix: "differentprefix") + @endpointRuleSet({ + "version": "1.0", + "rules": [{ + "conditions": [], + "type": "endpoint", + "endpoint": { + "url": "https://example.com", + "properties": {} + } + }], + "parameters": {} + }) + service TestService { + operations: [SayHello], + version: "1" + } + @endpoint(hostPrefix: "test123.{greeting}.") + operation SayHello { + input: SayHelloInput + } + structure SayHelloInput { + @required + @hostLabel + greeting: String + } + """.asSmithyModel() + clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + val moduleName = clientCodegenContext.moduleUseName() + rustCrate.integrationTest("test_endpoint_prefix") { + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn test_endpoint_prefix() { + use #{aws_smithy_client}::test_connection::capture_request; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::endpoint::EndpointPrefix; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::ConfigBag; + use std::sync::atomic::{AtomicU32, Ordering}; + use std::sync::{Arc, Mutex}; + use $moduleName::{ + config::interceptors::BeforeTransmitInterceptorContextRef, + config::Interceptor, + error::DisplayErrorContext, + {Client, Config}, + }; + + ##[derive(Clone, Debug, Default)] + struct TestInterceptor { + called: Arc, + last_endpoint_prefix: Arc>>, + } + impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + + fn read_before_transmit( + &self, + _context: &BeforeTransmitInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.called.fetch_add(1, Ordering::Relaxed); + if let Some(prefix) = cfg.load::() { + self.last_endpoint_prefix + .lock() + .unwrap() + .replace(prefix.clone()); + } + Ok(()) + } + } + + let (conn, _r) = capture_request(Some( + http::Response::builder() + .status(200) + .body(SdkBody::from("")) + .unwrap(), + )); + let interceptor = TestInterceptor::default(); + let config = Config::builder() + .http_connector(conn) + .interceptor(interceptor.clone()) + .build(); + let client = Client::from_conf(config); + let err = dbg!(client.say_hello().greeting("hey there!").send().await) + .expect_err("the endpoint should be invalid since it has an exclamation mark in it"); + let err_fmt = format!("{}", DisplayErrorContext(err)); + assert!( + err_fmt.contains("endpoint prefix could not be built"), + "expected '{}' to contain 'endpoint prefix could not be built'", + err_fmt + ); + + assert!( + interceptor.called.load(Ordering::Relaxed) == 0, + "the interceptor should not have been called since endpoint resolution failed" + ); + + dbg!(client.say_hello().greeting("hello").send().await) + .expect("hello is a valid endpoint prefix"); + assert!( + interceptor.called.load(Ordering::Relaxed) == 1, + "the interceptor should have been called" + ); + assert_eq!( + "test123.hello.", + interceptor + .last_endpoint_prefix + .lock() + .unwrap() + .clone() + .unwrap() + .as_str() + ); + } + """, + "aws_smithy_client" to CargoDependency.smithyClient(clientCodegenContext.runtimeConfig) + .toDevDependency().withFeature("test-util").toType(), + ) + } + } + } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt index 96eff3ad79..0726c6870c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt @@ -6,9 +6,11 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest @@ -23,6 +25,7 @@ internal class PaginatorGeneratorTest { } @readonly + @optionalAuth @paginated(inputToken: "nextToken", outputToken: "inner.token", pageSize: "maxResults", items: "inner.items") operation PaginatedList { @@ -31,6 +34,7 @@ internal class PaginatorGeneratorTest { } @readonly + @optionalAuth @paginated(inputToken: "nextToken", outputToken: "inner.token", pageSize: "maxResults", items: "inner.mapItems") operation PaginatedMap { @@ -67,8 +71,9 @@ internal class PaginatorGeneratorTest { } """.asSmithyModel() + // TODO(enableNewSmithyRuntimeCleanup): Remove this middleware test when launching @Test - fun `generate paginators that compile`() { + fun `generate paginators that compile with middleware`() { clientIntegrationTest(model) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("paginators_generated") { Attribute.AllowUnusedImports.render(this) @@ -76,4 +81,17 @@ internal class PaginatorGeneratorTest { } } } + + @Test + fun `generate paginators that compile`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + rustCrate.integrationTest("paginators_generated") { + Attribute.AllowUnusedImports.render(this) + rust("use ${clientCodegenContext.moduleUseName()}::operation::paginated_list::paginator::PaginatedListPaginator;") + } + } + } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndexTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndexTest.kt new file mode 100644 index 0000000000..cecf423588 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndexTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.util.lookup + +class SensitiveIndexTest { + val model = """ + namespace com.example + service TestService { + operations: [ + NotSensitive, + SensitiveInput, + SensitiveOutput, + NestedSensitiveInput, + NestedSensitiveOutput + ] + } + + @sensitive + structure Credentials { + username: String, + password: String + } + + operation NotSensitive { + input := { userId: String } + + output := { response: String } + } + + operation SensitiveInput { + input := { credentials: Credentials } + } + + operation SensitiveOutput { + output := { credentials: Credentials } + } + + operation NestedSensitiveInput { + input := { nested: Nested } + } + + operation NestedSensitiveOutput { + output := { nested: Nested } + } + + structure Nested { + inner: Inner + } + + structure Inner { + credentials: Credentials + } + """.asSmithyModel(smithyVersion = "2.0") + + @Test + fun `correctly identify operations`() { + val index = SensitiveIndex.of(model) + + data class TestCase(val shape: String, val sensitiveInput: Boolean, val sensitiveOutput: Boolean) + + val testCases = listOf( + TestCase("NotSensitive", sensitiveInput = false, sensitiveOutput = false), + TestCase("SensitiveInput", sensitiveInput = true, sensitiveOutput = false), + TestCase("SensitiveOutput", sensitiveInput = false, sensitiveOutput = true), + TestCase("NestedSensitiveInput", sensitiveInput = true, sensitiveOutput = false), + TestCase("NestedSensitiveOutput", sensitiveInput = false, sensitiveOutput = true), + ) + testCases.forEach { tc -> + assertEquals(tc.sensitiveInput, index.hasSensitiveInput(model.lookup("com.example#${tc.shape}")), "input: $tc") + assertEquals(tc.sensitiveOutput, index.hasSensitiveOutput(model.lookup("com.example#${tc.shape}")), "output: $tc ") + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt new file mode 100644 index 0000000000..1e02493212 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators.client + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.integrationTest + +class FluentClientGeneratorTest { + val model = """ + namespace com.example + use aws.protocols#awsJson1_0 + + @awsJson1_0 + service HelloService { + operations: [SayHello], + version: "1" + } + + @optionalAuth + operation SayHello { input: TestInput } + structure TestInput { + foo: String, + byteValue: Byte, + } + """.asSmithyModel() + + @Test + fun `send() future implements Send`() { + val test: (ClientCodegenContext, RustCrate) -> Unit = { codegenContext, rustCrate -> + rustCrate.integrationTest("send_future_is_send") { + val moduleName = codegenContext.moduleUseName() + rustTemplate( + """ + fn check_send(_: T) {} + + ##[test] + fn test() { + let connector = #{TestConnection}::<#{SdkBody}>::new(Vec::new()); + let config = $moduleName::Config::builder() + .endpoint_resolver("http://localhost:1234") + #{set_http_connector} + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + check_send(client.say_hello().send()); + } + """, + "TestConnection" to CargoDependency.smithyClient(codegenContext.runtimeConfig) + .toDevDependency() + .withFeature("test-util").toType() + .resolve("test_connection::TestConnection"), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "set_http_connector" to writable { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rust(".http_connector(connector.clone())") + } + }, + ) + } + } + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams, test = test) + clientIntegrationTest( + model, + TestCodegenSettings.orchestratorModeTestParams, + test = test, + ) + } + + @Test + fun `generate inner builders`() { + val test: (ClientCodegenContext, RustCrate) -> Unit = { codegenContext, rustCrate -> + rustCrate.integrationTest("inner_builder") { + val moduleName = codegenContext.moduleUseName() + rustTemplate( + """ + ##[test] + fn test() { + let connector = #{TestConnection}::<#{SdkBody}>::new(Vec::new()); + let config = $moduleName::Config::builder() + .endpoint_resolver("http://localhost:1234") + #{set_http_connector} + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + + let say_hello_fluent_builder = client.say_hello().byte_value(4).foo("hello!"); + assert_eq!(*say_hello_fluent_builder.get_foo(), Some("hello!".to_string())); + let input = say_hello_fluent_builder.as_input(); + assert_eq!(*input.get_byte_value(), Some(4)); + } + """, + "TestConnection" to CargoDependency.smithyClient(codegenContext.runtimeConfig) + .toDevDependency() + .withFeature("test-util").toType() + .resolve("test_connection::TestConnection"), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "set_http_connector" to writable { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rust(".http_connector(connector.clone())") + } + }, + ) + } + } + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams, test = test) + clientIntegrationTest( + model, + TestCodegenSettings.orchestratorModeTestParams, + test = test, + ) + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomizationTest.kt index cf65b897f4..2ae469608e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomizationTest.kt @@ -5,12 +5,24 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel class IdempotencyTokenProviderCustomizationTest { - @Test - fun `generates a valid config`() { - validateConfigCustomizations(IdempotencyTokenProviderCustomization()) + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val model = "namespace test".asSmithyModel() + val codegenContext = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations( + codegenContext, + IdempotencyTokenProviderCustomization(codegenContext), + ) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index 2aaa3e21dd..fc439f764c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -7,18 +7,27 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule -import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext +import software.amazon.smithy.rust.codegen.client.testutil.withEnableUserConfigurableRuntimePlugins +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup +import software.amazon.smithy.rust.codegen.core.util.toPascalCase internal class ServiceConfigGeneratorTest { @Test @@ -76,45 +85,133 @@ internal class ServiceConfigGeneratorTest { model.lookup("com.example#ResourceService").needsIdempotencyToken(model) shouldBe true } - @Test - fun `generate customizations as specified`() { - class ServiceCustomizer : NamedCustomization() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generate customizations as specified`(smithyRuntimeModeStr: String) { + class ServiceCustomizer(private val codegenContext: ClientCodegenContext) : + NamedCustomization() { + private val runtimeMode = codegenContext.smithyRuntimeMode + override fun section(section: ServiceConfig): Writable { return when (section) { ServiceConfig.ConfigStructAdditionalDocs -> emptySection - ServiceConfig.ConfigStruct -> writable { rust("config_field: u64,") } + ServiceConfig.ConfigStruct -> writable { + if (runtimeMode.generateMiddleware) { + rust("config_field: u64,") + } + } + ServiceConfig.ConfigImpl -> writable { - rust( - """ - pub fn config_field(&self) -> u64 { - self.config_field - } - """, - ) + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + ##[allow(missing_docs)] + pub fn config_field(&self) -> u64 { + self.config.load::<#{T}>().map(|u| u.0).unwrap() + } + """, + "T" to configParamNewtype( + "config_field".toPascalCase(), RuntimeType.U64.toSymbol(), + codegenContext.runtimeConfig, + ), + ) + } else { + rust( + """ + ##[allow(missing_docs)] + pub fn config_field(&self) -> u64 { + self.config_field + } + """, + ) + } + } + + ServiceConfig.BuilderStruct -> writable { + if (runtimeMode.generateMiddleware) { + rust("config_field: Option") + } + } + ServiceConfig.BuilderImpl -> writable { + if (runtimeMode.generateOrchestrator) { + rustTemplate( + """ + ##[allow(missing_docs)] + pub fn config_field(mut self, config_field: u64) -> Self { + self.config.store_put(#{T}(config_field)); + self + } + """, + "T" to configParamNewtype( + "config_field".toPascalCase(), RuntimeType.U64.toSymbol(), + codegenContext.runtimeConfig, + ), + ) + } } - ServiceConfig.BuilderStruct -> writable { rust("config_field: Option") } - ServiceConfig.BuilderImpl -> emptySection ServiceConfig.BuilderBuild -> writable { - rust("config_field: self.config_field.unwrap_or_default(),") + if (runtimeMode.generateMiddleware) { + rust("config_field: self.config_field.unwrap_or_default(),") + } } + else -> emptySection } } } - val sut = ServiceConfigGenerator(listOf(ServiceCustomizer())) - val symbolProvider = testSymbolProvider("namespace empty".asSmithyModel()) + + val model = "namespace empty".asSmithyModel() + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val codegenContext = testClientCodegenContext(model) + .withSmithyRuntimeMode(smithyRuntimeMode) + .withEnableUserConfigurableRuntimePlugins(true) + val sut = ServiceConfigGenerator(codegenContext, listOf(ServiceCustomizer(codegenContext))) + val symbolProvider = codegenContext.symbolProvider val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ClientRustModule.Config) { + project.withModule(ClientRustModule.config) { sut.render(this) - unitTest( - "set_config_fields", - """ - let mut builder = Config::builder(); - builder.config_field = Some(99); - let config = builder.build(); - assert_eq!(config.config_field, 99); - """, - ) + if (smithyRuntimeMode.generateOrchestrator) { + unitTest( + "set_config_fields", + """ + let builder = Config::builder().config_field(99); + let config = builder.build(); + assert_eq!(config.config_field(), 99); + """, + ) + + unitTest( + "set_runtime_plugin", + """ + use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; + use aws_smithy_types::config_bag::FrozenLayer; + + #[derive(Debug)] + struct TestRuntimePlugin; + + impl RuntimePlugin for TestRuntimePlugin { + fn config(&self) -> Option { + todo!("ExampleRuntimePlugin.config") + } + } + + let config = Config::builder() + .runtime_plugin(TestRuntimePlugin) + .build(); + assert_eq!(config.runtime_plugins.len(), 1); + """, + ) + } else { + unitTest( + "set_config_fields", + """ + let mut builder = Config::builder(); + builder.config_field = Some(99); + let config = builder.build(); + assert_eq!(config.config_field, 99); + """, + ) + } } project.compileAndTest() } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt index 1cbb274cfb..0f26fc7b42 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -6,45 +6,48 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.error import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.util.lookup internal class ServiceErrorGeneratorTest { - @Test - fun `top level errors are send + sync`() { - val model = """ - namespace com.example + private val model = """ + namespace com.example - use aws.protocols#restJson1 + use aws.protocols#restJson1 - @restJson1 - service HelloService { - operations: [SayHello], - version: "1" - } + @restJson1 + service HelloService { + operations: [SayHello], + version: "1" + } - @http(uri: "/", method: "POST") - operation SayHello { - input: EmptyStruct, - output: EmptyStruct, - errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] - } + @http(uri: "/", method: "POST") + operation SayHello { + input: EmptyStruct, + output: EmptyStruct, + errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] + } - structure EmptyStruct { } + structure EmptyStruct { } - @error("server") - structure SorryBusy { } + @error("server") + structure SorryBusy { } - @error("client") - structure CanYouRepeatThat { } + @error("client") + structure CanYouRepeatThat { } - @error("client") - @deprecated - structure MeDeprecated { } - """.asSmithyModel() + @error("client") + @deprecated + structure MeDeprecated { } + """.asSmithyModel() + @Test + fun `top level errors are send + sync`() { clientIntegrationTest(model) { codegenContext, rustCrate -> rustCrate.integrationTest("validate_errors") { rust( @@ -60,4 +63,25 @@ internal class ServiceErrorGeneratorTest { } } } + + @Test + fun `generates combined error enums`() { + clientIntegrationTest(model) { _, rustCrate -> + rustCrate.moduleFor(model.lookup("com.example#CanYouRepeatThat")) { + unitTest( + name = "generates_combined_error_enums", + test = """ + use std::error::Error as StdError; + use crate::Error; + use crate::operation::say_hello::SayHelloError; + + // Unhandled variants properly delegate source. + let error = Error::from(SayHelloError::unhandled("some other error")); + let source = error.source().expect("source should not be None"); + assert_eq!(format!("{}", source), "some other error"); + """, + ) + } + } + } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorMiddlewareTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorMiddlewareTest.kt new file mode 100644 index 0000000000..daea27a0b8 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorMiddlewareTest.kt @@ -0,0 +1,363 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol + +import io.kotest.matchers.string.shouldContain +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.escape +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap +import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.util.CommandError +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.outputShape +import java.nio.file.Path + +private class TestProtocolPayloadGenerator(private val body: String) : ProtocolPayloadGenerator { + override fun payloadMetadata(operationShape: OperationShape, additionalPayloadContext: AdditionalPayloadContext) = + ProtocolPayloadGenerator.PayloadMetadata(takesOwnership = false) + + override fun generatePayload( + writer: RustWriter, + shapeName: String, + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { + writer.writeWithNoFormatting(body) + } +} + +private class TestProtocolTraitImplGenerator( + private val codegenContext: ClientCodegenContext, + private val correctResponse: String, +) : HttpBoundProtocolTraitImplGenerator(codegenContext, RestJson(codegenContext)) { + private val symbolProvider = codegenContext.symbolProvider + + override fun generateTraitImpls( + operationWriter: RustWriter, + operationShape: OperationShape, + customizations: List, + ) { + operationWriter.rustTemplate( + """ + impl #{parse_strict} for ${operationShape.id.name}{ + type Output = Result<#{Output}, #{Error}>; + fn parse(&self, _response: &#{Response}<#{Bytes}>) -> Self::Output { + ${operationWriter.escape(correctResponse)} + } + } + """, + "parse_strict" to RuntimeType.parseStrictResponse(codegenContext.runtimeConfig), + "Output" to symbolProvider.toSymbol(operationShape.outputShape(codegenContext.model)), + "Error" to symbolProvider.symbolForOperationError(operationShape), + "Response" to RuntimeType.HttpResponse, + "Bytes" to RuntimeType.Bytes, + ) + } +} + +private class TestProtocolMakeOperationGenerator( + codegenContext: CodegenContext, + protocol: Protocol, + body: String, + private val httpRequestBuilder: String, +) : MakeOperationGenerator( + codegenContext, + protocol, + TestProtocolPayloadGenerator(body), + public = true, + includeDefaultPayloadHeaders = true, +) { + override fun createHttpRequest(writer: RustWriter, operationShape: OperationShape) { + writer.rust("#T::new()", RuntimeType.HttpRequestBuilder) + writer.writeWithNoFormatting(httpRequestBuilder) + } +} + +// A stubbed test protocol to do enable testing intentionally broken protocols +private class TestProtocolGenerator( + codegenContext: ClientCodegenContext, + protocol: Protocol, + httpRequestBuilder: String, + body: String, + correctResponse: String, +) : OperationGenerator( + codegenContext, + protocol, + TestProtocolMakeOperationGenerator(codegenContext, protocol, body, httpRequestBuilder), + TestProtocolPayloadGenerator(body), + TestProtocolTraitImplGenerator(codegenContext, correctResponse), +) + +private class TestProtocolFactory( + private val httpRequestBuilder: String, + private val body: String, + private val correctResponse: String, +) : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ClientCodegenContext): Protocol = RestJson(codegenContext) + + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): OperationGenerator { + return TestProtocolGenerator( + codegenContext, + protocol(codegenContext), + httpRequestBuilder, + body, + correctResponse, + ) + } + + override fun support(): ProtocolSupport { + return ProtocolSupport( + requestSerialization = true, + requestBodySerialization = true, + responseDeserialization = true, + errorDeserialization = true, + requestDeserialization = false, + requestBodyDeserialization = false, + responseSerialization = false, + errorSerialization = false, + ) + } +} + +// TODO(enableNewSmithyRuntime): Delete this file/test (replaced by ProtocolTestGeneratorTest for the orchestrator) +class ProtocolTestGeneratorMiddlewareTest { + private val model = """ + namespace com.example + + use aws.protocols#restJson1 + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + + @restJson1 + service HelloService { + operations: [SayHello], + version: "1" + } + + @http(method: "POST", uri: "/") + @httpRequestTests([ + { + id: "say_hello", + protocol: restJson1, + params: { + "greeting": "Hi", + "name": "Teddy", + "query": "Hello there" + }, + method: "POST", + uri: "/", + queryParams: [ + "Hi=Hello%20there" + ], + forbidQueryParams: [ + "goodbye" + ], + requireQueryParams: ["required"], + headers: { + "X-Greeting": "Hi", + }, + body: "{\"name\": \"Teddy\"}", + bodyMediaType: "application/json" + } + ]) + @httpResponseTests([{ + id: "basic_response_test", + protocol: restJson1, + documentation: "Parses operations with empty JSON bodies", + body: "{\"value\": \"hey there!\"}", + params: {"value": "hey there!"}, + bodyMediaType: "application/json", + headers: {"Content-Type": "application/x-amz-json-1.1"}, + code: 200, + }]) + operation SayHello { + input: SayHelloInput, + output: SayHelloOutput, + errors: [BadRequest] + } + + structure SayHelloOutput { + value: String + } + + @error("client") + structure BadRequest { + message: String + } + + structure SayHelloInput { + @httpHeader("X-Greeting") + greeting: String, + + @httpQuery("Hi") + query: String, + + name: String + } + """.asSmithyModel() + private val correctBody = """{"name": "Teddy"}""" + + /** + * Creates a fake HTTP implementation for SayHello & generates the protocol test + * + * Returns the [Path] the service was generated at, suitable for running `cargo test` + */ + private fun testService( + httpRequestBuilder: String, + body: String = "${correctBody.dq()}.to_string()", + correctResponse: String = """Ok(crate::operation::say_hello::SayHelloOutput::builder().value("hey there!").build())""", + ): Path { + val codegenDecorator = object : ClientCodegenDecorator { + override val name: String = "mock" + override val order: Byte = 0 + override fun classpathDiscoverable(): Boolean = false + override fun protocols( + serviceId: ShapeId, + currentProtocols: ProtocolMap, + ): ProtocolMap = + // Intentionally replace the builtin implementation of RestJson1 with our fake protocol + mapOf(RestJson1Trait.ID to TestProtocolFactory(httpRequestBuilder, body, correctResponse)) + } + return clientIntegrationTest( + model, + TestCodegenSettings.middlewareModeTestParams, + additionalDecorators = listOf(codegenDecorator), + ) + } + + @Test + fun `passing e2e protocol request test`() { + testService( + """ + .uri("/?Hi=Hello%20there&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + ) + } + + @Test + fun `test incorrect response parsing`() { + val err = assertThrows { + testService( + """ + .uri("/?Hi=Hello%20there&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + correctResponse = "Ok(crate::operation::say_hello::SayHelloOutput::builder().build())", + ) + } + + err.message shouldContain "basic_response_test_response ... FAILED" + } + + @Test + fun `test invalid body`() { + val err = assertThrows { + testService( + """ + .uri("/?Hi=Hello%20there&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + """"{}".to_string()""", + ) + } + + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "body did not match" + } + + @Test + fun `test invalid url parameter`() { + val err = assertThrows { + testService( + """ + .uri("/?Hi=INCORRECT&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + ) + } + // Verify the test actually ran + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "missing query param" + } + + @Test + fun `test forbidden url parameter`() { + val err = assertThrows { + testService( + """ + .uri("/?goodbye&Hi=Hello%20there&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + ) + } + // Verify the test actually ran + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "forbidden query param" + } + + @Test + fun `test required url parameter`() { + // Hard coded implementation for this 1 test + val err = assertThrows { + testService( + """ + .uri("/?Hi=Hello%20there") + .header("X-Greeting", "Hi") + .method("POST") + """, + ) + } + + // Verify the test actually ran + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "required query param missing" + } + + @Test + fun `invalid header`() { + val err = assertThrows { + testService( + """ + .uri("/?Hi=Hello%20there&required") + // should be "Hi" + .header("X-Greeting", "Hey") + .method("POST") + """, + ) + } + + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "invalid header value" + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index d0d271231a..066c25b8fd 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -8,131 +8,115 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol import io.kotest.matchers.string.shouldContain import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import software.amazon.smithy.aws.traits.protocols.RestJson1Trait import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.rustlang.escape -import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory -import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap -import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.dq -import software.amazon.smithy.rust.codegen.core.util.outputShape import java.nio.file.Path - -private class TestProtocolPayloadGenerator(private val body: String) : ProtocolPayloadGenerator { - override fun payloadMetadata(operationShape: OperationShape) = - ProtocolPayloadGenerator.PayloadMetadata(takesOwnership = false) - - override fun generatePayload(writer: RustWriter, self: String, operationShape: OperationShape) { - writer.writeWithNoFormatting(body) - } -} - -private class TestProtocolTraitImplGenerator( - private val codegenContext: ClientCodegenContext, - private val correctResponse: String, -) : HttpBoundProtocolTraitImplGenerator(codegenContext, RestJson(codegenContext)) { - private val symbolProvider = codegenContext.symbolProvider - - override fun generateTraitImpls( - operationWriter: RustWriter, - operationShape: OperationShape, - customizations: List, - ) { - operationWriter.rustTemplate( - """ - impl #{parse_strict} for ${operationShape.id.name}{ - type Output = Result<#{Output}, #{Error}>; - fn parse(&self, _response: &#{Response}<#{Bytes}>) -> Self::Output { - ${operationWriter.escape(correctResponse)} - } +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType as RT + +private class TestServiceRuntimePluginCustomization( + private val context: ClientCodegenContext, + private val fakeRequestBuilder: String, + private val fakeRequestBody: String, +) : ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { + val rc = context.runtimeConfig + section.registerInterceptor(rc, this) { + rustTemplate( + """ + { + ##[derive(::std::fmt::Debug)] + struct TestInterceptor; + impl #{Interceptor} for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + + fn modify_before_retry_loop( + &self, + context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, + _rc: &#{RuntimeComponents}, + _cfg: &mut #{ConfigBag}, + ) -> #{Result}<(), #{BoxError}> { + // Replace the serialized request + let mut fake_req = ::http::Request::builder() + $fakeRequestBuilder + .body(#{SdkBody}::from($fakeRequestBody)) + .expect("valid request"); + ::std::mem::swap( + context.request_mut(), + &mut fake_req, + ); + Ok(()) + } + } + + TestInterceptor + } + """, + *preludeScope, + "BeforeTransmitInterceptorContextMut" to RT.beforeTransmitInterceptorContextMut(rc), + "BoxError" to RT.boxError(rc), + "ConfigBag" to RT.configBag(rc), + "Interceptor" to RT.interceptor(rc), + "RuntimeComponents" to RT.runtimeComponents(rc), + "SdkBody" to RT.sdkBody(rc), + ) } - """, - "parse_strict" to RuntimeType.parseStrictResponse(codegenContext.runtimeConfig), - "Output" to symbolProvider.toSymbol(operationShape.outputShape(codegenContext.model)), - "Error" to symbolProvider.symbolForOperationError(operationShape), - "Response" to RuntimeType.HttpResponse, - "Bytes" to RuntimeType.Bytes, - ) - } -} - -private class TestProtocolMakeOperationGenerator( - codegenContext: CodegenContext, - protocol: Protocol, - body: String, - private val httpRequestBuilder: String, -) : MakeOperationGenerator( - codegenContext, - protocol, - TestProtocolPayloadGenerator(body), - public = true, - includeDefaultPayloadHeaders = true, -) { - override fun createHttpRequest(writer: RustWriter, operationShape: OperationShape) { - writer.rust("#T::new()", RuntimeType.HttpRequestBuilder) - writer.writeWithNoFormatting(httpRequestBuilder) + } } } -// A stubbed test protocol to do enable testing intentionally broken protocols -private class TestProtocolGenerator( - codegenContext: ClientCodegenContext, - protocol: Protocol, - httpRequestBuilder: String, - body: String, - correctResponse: String, -) : ClientProtocolGenerator( - codegenContext, - protocol, - TestProtocolMakeOperationGenerator(codegenContext, protocol, body, httpRequestBuilder), - TestProtocolPayloadGenerator(body), - TestProtocolTraitImplGenerator(codegenContext, correctResponse), -) - -private class TestProtocolFactory( - private val httpRequestBuilder: String, - private val body: String, - private val correctResponse: String, -) : ProtocolGeneratorFactory { - override fun protocol(codegenContext: ClientCodegenContext): Protocol = RestJson(codegenContext) - - override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): ClientProtocolGenerator { - return TestProtocolGenerator( - codegenContext, - protocol(codegenContext), - httpRequestBuilder, - body, - correctResponse, - ) - } - - override fun support(): ProtocolSupport { - return ProtocolSupport( - requestSerialization = true, - requestBodySerialization = true, - responseDeserialization = true, - errorDeserialization = true, - requestDeserialization = false, - requestBodyDeserialization = false, - responseSerialization = false, - errorSerialization = false, - ) +private class TestOperationCustomization( + private val context: ClientCodegenContext, + private val fakeOutput: String, +) : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + val rc = context.runtimeConfig + if (section is OperationSection.AdditionalRuntimePluginConfig) { + rustTemplate( + """ + // Override the default response deserializer with our fake output + ##[derive(::std::fmt::Debug)] + struct TestDeser; + impl #{ResponseDeserializer} for TestDeser { + fn deserialize_nonstreaming( + &self, + _response: &#{HttpResponse}, + ) -> #{Result}<#{Output}, #{OrchestratorError}<#{Error}>> { + let fake_out: #{Result}< + crate::operation::say_hello::SayHelloOutput, + crate::operation::say_hello::SayHelloError, + > = $fakeOutput; + fake_out + .map(|o| #{Output}::erase(o)) + .map_err(|e| #{OrchestratorError}::operation(#{Error}::erase(e))) + } + } + cfg.store_put(#{SharedResponseDeserializer}::new(TestDeser)); + """, + *preludeScope, + "SharedResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::SharedResponseDeserializer"), + "Error" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Error"), + "HttpResponse" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::HttpResponse"), + "OrchestratorError" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::OrchestratorError"), + "Output" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Output"), + "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::ResponseDeserializer"), + ) + } } } @@ -219,22 +203,34 @@ class ProtocolTestGeneratorTest { * Returns the [Path] the service was generated at, suitable for running `cargo test` */ private fun testService( - httpRequestBuilder: String, - body: String = "${correctBody.dq()}.to_string()", - correctResponse: String = """Ok(crate::operation::say_hello::SayHelloOutput::builder().value("hey there!").build())""", + fakeRequestBuilder: String, + fakeRequestBody: String = "${correctBody.dq()}.to_string()", + fakeOutput: String = """Ok(crate::operation::say_hello::SayHelloOutput::builder().value("hey there!").build())""", ): Path { val codegenDecorator = object : ClientCodegenDecorator { override val name: String = "mock" override val order: Byte = 0 override fun classpathDiscoverable(): Boolean = false - override fun protocols( - serviceId: ShapeId, - currentProtocols: ProtocolMap, - ): ProtocolMap = - // Intentionally replace the builtin implementation of RestJson1 with our fake protocol - mapOf(RestJson1Trait.ID to TestProtocolFactory(httpRequestBuilder, body, correctResponse)) + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + TestServiceRuntimePluginCustomization( + codegenContext, + fakeRequestBuilder, + fakeRequestBody, + ) + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations + TestOperationCustomization(codegenContext, fakeOutput) } - return clientIntegrationTest(model, additionalDecorators = listOf(codegenDecorator)) + return clientIntegrationTest( + model, + additionalDecorators = listOf(codegenDecorator), + ) } @Test @@ -250,14 +246,14 @@ class ProtocolTestGeneratorTest { @Test fun `test incorrect response parsing`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=Hello%20there&required") .header("X-Greeting", "Hi") .method("POST") """, - correctResponse = "Ok(crate::operation::say_hello::SayHelloOutput::builder().build())", + fakeOutput = "Ok(crate::operation::say_hello::SayHelloOutput::builder().build())", ) } @@ -266,7 +262,7 @@ class ProtocolTestGeneratorTest { @Test fun `test invalid body`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=Hello%20there&required") @@ -283,7 +279,7 @@ class ProtocolTestGeneratorTest { @Test fun `test invalid url parameter`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=INCORRECT&required") @@ -299,7 +295,7 @@ class ProtocolTestGeneratorTest { @Test fun `test forbidden url parameter`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?goodbye&Hi=Hello%20there&required") @@ -316,7 +312,7 @@ class ProtocolTestGeneratorTest { @Test fun `test required url parameter`() { // Hard coded implementation for this 1 test - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=Hello%20there") @@ -333,7 +329,7 @@ class ProtocolTestGeneratorTest { @Test fun `invalid header`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=Hello%20there&required") diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt index c5b2470dd9..e655777be7 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt @@ -6,10 +6,15 @@ package software.amazon.smithy.rust.codegen.client.smithy.protocols import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest +import software.amazon.smithy.rust.codegen.core.util.lookup class AwsQueryCompatibleTest { @Test @@ -48,7 +53,168 @@ class AwsQueryCompatibleTest { } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model) { context, rustCrate -> + val operation: OperationShape = context.model.lookup("test#SomeOperation") + rustCrate.withModule(context.symbolProvider.moduleForShape(operation)) { + rustTemplate( + """ + ##[cfg(test)] + ##[#{tokio}::test] + async fn should_parse_code_and_type_fields() { + use #{smithy_client}::test_connection::infallible_connection_fn; + use aws_smithy_http::body::SdkBody; + + let response = |_: http::Request| { + http::Response::builder() + .header( + "x-amzn-query-error", + http::HeaderValue::from_static("AWS.SimpleQueueService.NonExistentQueue;Sender"), + ) + .status(400) + .body( + SdkBody::from( + r##"{ + "__type": "com.amazonaws.sqs##QueueDoesNotExist", + "message": "Some user-visible message" + }"## + ) + ) + .unwrap() + }; + let client = crate::Client::from_conf( + crate::Config::builder() + .http_connector(infallible_connection_fn(response)) + .endpoint_url("http://localhost:1234") + .build() + ); + let error = dbg!(client.some_operation().send().await).err().unwrap().into_service_error(); + assert_eq!( + Some("AWS.SimpleQueueService.NonExistentQueue"), + error.meta().code(), + ); + assert_eq!(Some("Sender"), error.meta().extra("type")); + } + """, + "smithy_client" to CargoDependency.smithyClient(context.runtimeConfig) + .toDevDependency().withFeature("test-util").toType(), + "tokio" to CargoDependency.Tokio.toType(), + ) + } + } + } + + @Test + fun `aws-query-compatible json without aws query error should allow for retrieving error code from payload`() { + val model = """ + namespace test + use aws.protocols#awsJson1_0 + use aws.protocols#awsQueryCompatible + + @awsQueryCompatible + @awsJson1_0 + service TestService { + version: "2023-02-20", + operations: [SomeOperation] + } + + operation SomeOperation { + input: SomeOperationInputOutput, + output: SomeOperationInputOutput, + errors: [InvalidThingException], + } + + structure SomeOperationInputOutput { + a: String, + b: Integer + } + + @error("client") + structure InvalidThingException { + message: String + } + """.asSmithyModel() + + clientIntegrationTest(model) { context, rustCrate -> + val operation: OperationShape = context.model.lookup("test#SomeOperation") + rustCrate.withModule(context.symbolProvider.moduleForShape(operation)) { + rustTemplate( + """ + ##[cfg(test)] + ##[#{tokio}::test] + async fn should_parse_code_from_payload() { + use #{smithy_client}::test_connection::infallible_connection_fn; + use aws_smithy_http::body::SdkBody; + + let response = |_: http::Request| { + http::Response::builder() + .status(400) + .body( + SdkBody::from( + r##"{ + "__type": "com.amazonaws.sqs##QueueDoesNotExist", + "message": "Some user-visible message" + }"##, + ) + ) + .unwrap() + }; + let client = crate::Client::from_conf( + crate::Config::builder() + .http_connector(infallible_connection_fn(response)) + .endpoint_url("http://localhost:1234") + .build() + ); + let error = dbg!(client.some_operation().send().await).err().unwrap().into_service_error(); + assert_eq!(Some("QueueDoesNotExist"), error.meta().code()); + assert_eq!(None, error.meta().extra("type")); + } + """, + "smithy_client" to CargoDependency.smithyClient(context.runtimeConfig) + .toDevDependency().withFeature("test-util").toType(), + "tokio" to CargoDependency.Tokio.toType(), + ) + } + } + } + + // TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced above for orchestrator) + @Test + fun `middleware - aws-query-compatible json with aws query error should allow for retrieving error code and type from custom header`() { + val model = """ + namespace test + use aws.protocols#awsJson1_0 + use aws.protocols#awsQueryCompatible + use aws.protocols#awsQueryError + + @awsQueryCompatible + @awsJson1_0 + service TestService { + version: "2023-02-20", + operations: [SomeOperation] + } + + operation SomeOperation { + input: SomeOperationInputOutput, + output: SomeOperationInputOutput, + errors: [InvalidThingException], + } + + structure SomeOperationInputOutput { + a: String, + b: Integer + } + + @awsQueryError( + code: "InvalidThing", + httpResponseCode: 400, + ) + @error("client") + structure InvalidThingException { + message: String + } + """.asSmithyModel() + + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("should_parse_code_and_type_fields") { rust( @@ -87,8 +253,9 @@ class AwsQueryCompatibleTest { } } + // TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced above for orchestrator) @Test - fun `aws-query-compatible json without aws query error should allow for retrieving error code from payload`() { + fun `middleware - aws-query-compatible json without aws query error should allow for retrieving error code from payload`() { val model = """ namespace test use aws.protocols#awsJson1_0 @@ -118,7 +285,7 @@ class AwsQueryCompatibleTest { } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("should_parse_code_from_payload") { rust( diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt new file mode 100644 index 0000000000..a54397ff59 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.testutil + +import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.model.node.StringNode +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams + +object TestCodegenSettings { + // TODO(enableNewSmithyRuntimeCleanup): Delete this when removing `enableNewSmithyRuntime` feature gate + fun middlewareMode(): ObjectNode = ObjectNode.objectNodeBuilder() + .withMember( + "codegen", + ObjectNode.objectNodeBuilder() + .withMember("enableNewSmithyRuntime", StringNode.from("middleware")).build(), + ) + .build() + + // TODO(enableNewSmithyRuntimeCleanup): Delete this when removing `enableNewSmithyRuntime` feature gate + fun orchestratorMode(): ObjectNode = ObjectNode.objectNodeBuilder() + .withMember( + "codegen", + ObjectNode.objectNodeBuilder() + .withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")).build(), + ) + .build() + + // TODO(enableNewSmithyRuntimeCleanup): Delete this when removing `enableNewSmithyRuntime` feature gate + val middlewareModeTestParams get(): IntegrationTestParams = + IntegrationTestParams(additionalSettings = middlewareMode()) + + // TODO(enableNewSmithyRuntimeCleanup): Delete this when removing `enableNewSmithyRuntime` feature gate + val orchestratorModeTestParams get(): IntegrationTestParams = + IntegrationTestParams(additionalSettings = orchestratorMode()) +} diff --git a/codegen-core/build.gradle.kts b/codegen-core/build.gradle.kts index 393eb4dfa7..556088f8a0 100644 --- a/codegen-core/build.gradle.kts +++ b/codegen-core/build.gradle.kts @@ -112,12 +112,11 @@ if (isTestingEnabled.toBoolean()) { tasks.test { useJUnitPlatform() testLogging { - events("passed", "skipped", "failed") + events("failed") exceptionFormat = TestExceptionFormat.FULL showCauses = true showExceptions = true showStackTraces = true - showStandardStreams = true } } diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index 2651b77335..c30c669b16 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -11,8 +11,13 @@ use smithy.framework#ValidationException service ConstraintsService { operations: [ ConstrainedShapesOperation, + // See https://github.com/awslabs/smithy-rs/issues/2760 for why testing operations reaching + // constrained shapes that only lie in the output is important. + ConstrainedShapesOnlyInOutputOperation, ConstrainedHttpBoundShapesOperation, + ConstrainedHttpPayloadBoundShapeOperation, ConstrainedRecursiveShapesOperation, + // `httpQueryParams` and `httpPrefixHeaders` are structurually // exclusive, so we need one operation per target shape type // combination. @@ -49,6 +54,11 @@ operation ConstrainedShapesOperation { errors: [ValidationException] } +@http(uri: "/constrained-shapes-only-in-output-operation", method: "POST") +operation ConstrainedShapesOnlyInOutputOperation { + output: ConstrainedShapesOnlyInOutputOperationOutput, +} + @http( uri: "/constrained-http-bound-shapes-operation/{rangeIntegerLabel}/{rangeShortLabel}/{rangeLongLabel}/{rangeByteLabel}/{lengthStringLabel}/{enumStringLabel}", method: "POST" @@ -59,6 +69,13 @@ operation ConstrainedHttpBoundShapesOperation { errors: [ValidationException] } +@http(uri: "/constrained-http-payload-bound-shape-operation", method: "POST") +operation ConstrainedHttpPayloadBoundShapeOperation { + input: ConstrainedHttpPayloadBoundShapeOperationInputOutput, + output: ConstrainedHttpPayloadBoundShapeOperationInputOutput, + errors: [ValidationException] +} + @http(uri: "/constrained-recursive-shapes-operation", method: "POST") operation ConstrainedRecursiveShapesOperation { input: ConstrainedRecursiveShapesOperationInputOutput, @@ -311,6 +328,12 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { enumStringListQuery: ListOfEnumString, } +structure ConstrainedHttpPayloadBoundShapeOperationInputOutput { + @required + @httpPayload + httpPayloadBoundConstrainedShape: ConA +} + structure QueryParamsTargetingMapOfPatternStringOperationInputOutput { @httpQueryParams mapOfPatternString: MapOfPatternString @@ -454,6 +477,7 @@ structure ConA { conBList: ConBList, lengthList: LengthList, + sensitiveLengthList: SensitiveLengthList, conBSet: ConBSet, @@ -866,6 +890,14 @@ list LengthList { member: String } +@length(max: 69) +list SensitiveLengthList { + member: SensitiveStructure +} + +@sensitive +structure SensitiveStructure { } + set ConBSet { member: ConBSetInner } @@ -911,3 +943,31 @@ map MapOfListOfListOfConB { key: String, value: ConBList } + +structure ConstrainedShapesOnlyInOutputOperationOutput { + list: ConstrainedListInOutput + map: ConstrainedMapInOutput + // Unions were not affected by + // https://github.com/awslabs/smithy-rs/issues/2760, but testing anyway for + // good measure. + union: ConstrainedUnionInOutput +} + +@length(min: 69) +list ConstrainedListInOutput { + member: ConstrainedUnionInOutput +} + +@length(min: 69) +map ConstrainedMapInOutput { + key: String + value: TransitivelyConstrainedStructureInOutput +} + +union ConstrainedUnionInOutput { + structure: TransitivelyConstrainedStructureInOutput +} + +structure TransitivelyConstrainedStructureInOutput { + lengthString: LengthString +} diff --git a/codegen-core/common-test-models/pokemon-awsjson.smithy b/codegen-core/common-test-models/pokemon-awsjson.smithy index 7d3a9ae021..12e455ecdd 100644 --- a/codegen-core/common-test-models/pokemon-awsjson.smithy +++ b/codegen-core/common-test-models/pokemon-awsjson.smithy @@ -4,7 +4,7 @@ $version: "1.0" // This is a temporary model to test AwsJson 1.0 with @streaming. // This model will be removed when protocol tests support @streaming. -namespace com.aws.example.rust +namespace com.aws.example use aws.protocols#awsJson1_0 use smithy.framework#ValidationException diff --git a/codegen-core/common-test-models/pokemon.smithy b/codegen-core/common-test-models/pokemon.smithy index 20e56e381a..014ee61c41 100644 --- a/codegen-core/common-test-models/pokemon.smithy +++ b/codegen-core/common-test-models/pokemon.smithy @@ -1,6 +1,6 @@ $version: "1.0" -namespace com.aws.example.rust +namespace com.aws.example use aws.protocols#restJson1 use smithy.framework#ValidationException @@ -20,7 +20,8 @@ service PokemonService { GetServerStatistics, DoNothing, CapturePokemon, - CheckHealth + CheckHealth, + StreamPokemonRadio ], } @@ -146,3 +147,19 @@ structure MasterBallUnsuccessful { @error("client") structure ThrottlingError {} + +/// Fetch a radio song from the database and stream it back as a playable audio. +@readonly +@http(uri: "/radio", method: "GET") +operation StreamPokemonRadio { + output: StreamPokemonRadioOutput +} + +@output +structure StreamPokemonRadioOutput { + @httpPayload + data: StreamingBlob +} + +@streaming +blob StreamingBlob diff --git a/codegen-core/common-test-models/rest-json-extras.smithy b/codegen-core/common-test-models/rest-json-extras.smithy index b9946baa81..ff92f36c6b 100644 --- a/codegen-core/common-test-models/rest-json-extras.smithy +++ b/codegen-core/common-test-models/rest-json-extras.smithy @@ -81,16 +81,12 @@ service RestJsonExtras { appliesTo: "client", }, { - documentation: """ - Upper case error modeled lower case. - Servers render the full shape ID (including namespace), since some - existing clients rely on it to deserialize the error shape and fail - if only the shape name is present.""", + documentation: "Upper case error modeled lower case.", id: "ServiceLevelErrorServer", protocol: "aws.protocols#restJson1", code: 500, body: "{}", - headers: { "X-Amzn-Errortype": "aws.protocoltests.restjson#ExtraError" }, + headers: { "X-Amzn-Errortype": "ExtraError" }, params: {}, appliesTo: "server", } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index f10b828cfe..73a5e9ebbe 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -13,9 +13,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.dq import java.nio.file.Path -sealed class DependencyScope { - object Dev : DependencyScope() - object Compile : DependencyScope() +enum class DependencyScope { + Build, + CfgUnstable, + Compile, + Dev, } sealed class DependencyLocation @@ -102,8 +104,12 @@ class InlineDependency( CargoDependency.Http, ) - fun idempotencyToken() = - forInlineableRustFile("idempotency_token", CargoDependency.FastRand) + fun idempotencyToken(runtimeConfig: RuntimeConfig) = + forInlineableRustFile( + "idempotency_token", + CargoDependency.FastRand, + CargoDependency.smithyTypes(runtimeConfig), + ) fun ec2QueryErrors(runtimeConfig: RuntimeConfig): InlineDependency = forInlineableRustFile("ec2_query_errors", CargoDependency.smithyXml(runtimeConfig)) @@ -114,6 +120,13 @@ class InlineDependency( fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig): InlineDependency = forInlineableRustFile("rest_xml_unwrapped_errors", CargoDependency.smithyXml(runtimeConfig)) + fun serializationSettings(runtimeConfig: RuntimeConfig): InlineDependency = forInlineableRustFile( + "serialization_settings", + CargoDependency.Http, + CargoDependency.smithyHttp(runtimeConfig), + CargoDependency.smithyTypes(runtimeConfig), + ) + fun constrained(): InlineDependency = InlineDependency.forRustFile(ConstrainedModule, "/inlineable/src/constrained.rs") } @@ -123,6 +136,8 @@ fun InlineDependency.toType() = RuntimeType(module.fullyQualifiedPath(), this) data class Feature(val name: String, val default: Boolean, val deps: List) +val DEV_ONLY_FEATURES = setOf("test-util") + /** * A dependency on an internal or external Cargo Crate */ @@ -137,6 +152,12 @@ data class CargoDependency( ) : RustDependency(name) { val key: Triple get() = Triple(name, location, scope) + init { + if (scope != DependencyScope.Dev && DEV_ONLY_FEATURES.any { features.contains(it) }) { + throw IllegalArgumentException("The `test-util` feature cannot be used outside of DependencyScope.Dev") + } + } + fun withFeature(feature: String): CargoDependency { return copy(features = features.toMutableSet().apply { add(feature) }) } @@ -192,11 +213,12 @@ data class CargoDependency( attribs.add("features = [${joinToString(",") { it.dq() }}]") } } - return "$name = { ${attribs.joinToString(",")} }" + attribs.add("scope = $scope") + return "$name = { ${attribs.joinToString(", ")} }" } fun toType(): RuntimeType { - return RuntimeType(rustName, this) + return RuntimeType("::$rustName", this) } companion object { @@ -204,11 +226,11 @@ data class CargoDependency( val Url: CargoDependency = CargoDependency("url", CratesIo("2.3.1")) val Bytes: CargoDependency = CargoDependency("bytes", CratesIo("1.0.0")) val BytesUtils: CargoDependency = CargoDependency("bytes-utils", CratesIo("0.1.0")) - val FastRand: CargoDependency = CargoDependency("fastrand", CratesIo("1.8.0")) + val FastRand: CargoDependency = CargoDependency("fastrand", CratesIo("2.0.0")) val Hex: CargoDependency = CargoDependency("hex", CratesIo("0.4.3")) val Http: CargoDependency = CargoDependency("http", CratesIo("0.2.9")) val HttpBody: CargoDependency = CargoDependency("http-body", CratesIo("0.4.4")) - val Hyper: CargoDependency = CargoDependency("hyper", CratesIo("0.14.12")) + val Hyper: CargoDependency = CargoDependency("hyper", CratesIo("0.14.26")) val HyperWithStream: CargoDependency = Hyper.withFeature("stream") val LazyStatic: CargoDependency = CargoDependency("lazy_static", CratesIo("1.4.0")) val Md5: CargoDependency = CargoDependency("md-5", CratesIo("0.10.0"), rustName = "md5") @@ -220,11 +242,13 @@ data class CargoDependency( val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) // Test-only dependencies + val Approx: CargoDependency = CargoDependency("approx", CratesIo("0.5.1"), DependencyScope.Dev) val AsyncStd: CargoDependency = CargoDependency("async-std", CratesIo("1.12.0"), DependencyScope.Dev) val AsyncStream: CargoDependency = CargoDependency("async-stream", CratesIo("0.3.0"), DependencyScope.Dev) val Criterion: CargoDependency = CargoDependency("criterion", CratesIo("0.4.0"), DependencyScope.Dev) val FuturesCore: CargoDependency = CargoDependency("futures-core", CratesIo("0.3.25"), DependencyScope.Dev) - val FuturesUtil: CargoDependency = CargoDependency("futures-util", CratesIo("0.3.25"), DependencyScope.Dev, defaultFeatures = false) + val FuturesUtil: CargoDependency = + CargoDependency("futures-util", CratesIo("0.3.25"), DependencyScope.Dev, defaultFeatures = false) val HdrHistogram: CargoDependency = CargoDependency("hdrhistogram", CratesIo("7.5.2"), DependencyScope.Dev) val Hound: CargoDependency = CargoDependency("hound", CratesIo("3.4.0"), DependencyScope.Dev) val PrettyAssertions: CargoDependency = @@ -273,8 +297,15 @@ data class CargoDependency( fun smithyQuery(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-query") fun smithyRuntime(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime") + .withFeature("client") fun smithyRuntimeApi(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime-api") + .withFeature("client") + fun smithyRuntimeApiTestUtil(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).toDevDependency().withFeature("test-util") fun smithyTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-types") fun smithyXml(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-xml") + + // behind feature-gate + val Serde = CargoDependency("serde", CratesIo("1.0"), features = setOf("derive"), scope = DependencyScope.CfgUnstable) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustModule.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustModule.kt index b8c3237e41..78dee92dae 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustModule.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustModule.kt @@ -79,14 +79,19 @@ sealed class RustModule { } /** Creates a new public module */ - fun public(name: String, parent: RustModule = LibRs, documentationOverride: String? = null): LeafModule = - new( - name, - visibility = Visibility.PUBLIC, - inline = false, - parent = parent, - documentationOverride = documentationOverride, - ) + fun public( + name: String, + parent: RustModule = LibRs, + documentationOverride: String? = null, + additionalAttributes: List = emptyList(), + ): LeafModule = new( + name, + visibility = Visibility.PUBLIC, + inline = false, + parent = parent, + documentationOverride = documentationOverride, + additionalAttributes = additionalAttributes, + ) /** Creates a new private module */ fun private(name: String, parent: RustModule = LibRs): LeafModule = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index a0f684a77b..798218ab96 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.rustlang import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.serde import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.dq @@ -172,6 +173,18 @@ sealed class RustType { } data class Opaque(override val name: kotlin.String, override val namespace: kotlin.String? = null) : RustType() + + /** + * Represents application of a Rust type with the given arguments. + * + * For example, we can represent `HashMap` as + * `RustType.Application(RustType.Opaque("HashMap"), listOf(RustType.String, RustType.Integer(64)))`. + * This helps us to separate the type and the arguments which is useful in methods like [qualifiedName]. + */ + data class Application(val type: RustType, val args: List) : RustType() { + override val name = type.name + override val namespace = type.namespace + } } /** @@ -187,7 +200,7 @@ fun RustType.qualifiedName(): String { /** Format this Rust type as an `impl Into` */ fun RustType.implInto(fullyQualified: Boolean = true): String { - return "impl Into<${this.render(fullyQualified)}>" + return "impl ${RuntimeType.Into.fullyQualifiedName()}<${this.render(fullyQualified)}>" } /** Format this Rust type so that it may be used as an argument type in a function definition */ @@ -242,7 +255,10 @@ fun RustType.render(fullyQualified: Boolean = true): String { "&${this.lifetime?.let { "'$it" } ?: ""} ${this.member.render(fullyQualified)}" } } - + is RustType.Application -> { + val args = this.args.joinToString(", ") { it.render(fullyQualified) } + "${this.name}<$args>" + } is RustType.Option -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Box -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Dyn -> "${this.name} ${this.member.render(fullyQualified)}" @@ -370,11 +386,17 @@ data class RustMetadata( this.copy(derives = derives - withoutDerives.toSet()) fun renderAttributes(writer: RustWriter): RustMetadata { - additionalAttributes.forEach { + val (deriveHelperAttrs, otherAttrs) = additionalAttributes.partition { it.isDeriveHelper } + otherAttrs.forEach { it.render(writer) } + Attribute(derive(derives)).render(writer) + // Derive helper attributes must come after derive, see https://github.com/rust-lang/rust/issues/79202 + deriveHelperAttrs.forEach { + it.render(writer) + } return this } @@ -401,15 +423,6 @@ data class RustMetadata( fun hasDebugDerive(): Boolean { return derives.contains(RuntimeType.Debug) } - - companion object { - val TestModule = RustMetadata( - visibility = Visibility.PRIVATE, - additionalAttributes = listOf( - Attribute.CfgTest, - ), - ) - } } data class Argument(val argument: String, val value: String, val type: String) @@ -435,17 +448,22 @@ enum class AttributeKind { * [Attributes](https://doc.rust-lang.org/reference/attributes.html) are general free form metadata * that are interpreted by the compiler. * + * If the attribute is a "derive helper", such as `#[serde]`, set `isDeriveHelper` to `true` so it is sorted correctly after + * the derive attribute is rendered. (See https://github.com/rust-lang/rust/issues/79202 for why sorting matters.) + * * For example: * ```rust + * #[allow(missing_docs)] // <-- this is an attribute, and it is not a derive helper * #[derive(Clone, PartialEq, Serialize)] // <-- this is an attribute - * #[serde(serialize_with = "abc")] // <-- this is an attribute + * #[serde(serialize_with = "abc")] // <-- this attribute is a derive helper because the `Serialize` derive uses it * struct Abc { * a: i64 * } * ``` */ -class Attribute(val inner: Writable) { +class Attribute(val inner: Writable, val isDeriveHelper: Boolean = false) { constructor(str: String) : this(writable(str)) + constructor(str: String, isDeriveHelper: Boolean) : this(writable(str), isDeriveHelper) constructor(runtimeType: RuntimeType) : this(runtimeType.writable) fun render(writer: RustWriter, attributeKind: AttributeKind = AttributeKind.Outer) { @@ -458,6 +476,23 @@ class Attribute(val inner: Writable) { } } + // These were supposed to be a part of companion object but we decided to move it out to here to avoid NPE + // You can find the discussion here. + // https://github.com/awslabs/smithy-rs/discussions/2248 + public fun SerdeSerialize(): Attribute { + return Attribute(cfgAttr(all(writable("aws_sdk_unstable"), feature("serde-serialize")), derive(RuntimeType.SerdeSerialize))) + } + public fun SerdeDeserialize(): Attribute { + return Attribute(cfgAttr(all(writable("aws_sdk_unstable"), feature("serde-deserialize")), derive(RuntimeType.SerdeDeserialize))) + } + public fun SerdeSkip(): Attribute { + return Attribute(cfgAttr(all(writable("aws_sdk_unstable"), any(feature("serde-serialize"), feature("serde-deserialize"))), serde("skip"))) + } + + public fun SerdeSerializeOrDeserialize(): Attribute { + return Attribute(cfg(all(writable("aws_sdk_unstable"), any(feature("serde-serialize"), feature("serde-deserialize"))))) + } + companion object { val AllowClippyBoxedLocal = Attribute(allow("clippy::boxed_local")) val AllowClippyLetAndReturn = Attribute(allow("clippy::let_and_return")) @@ -474,6 +509,7 @@ class Attribute(val inner: Writable) { val AllowNonSnakeCase = Attribute(allow("non_snake_case")) val AllowUnreachableCode = Attribute(allow("unreachable_code")) val AllowUnreachablePatterns = Attribute(allow("unreachable_patterns")) + val AllowUnused = Attribute(allow("unused")) val AllowUnusedImports = Attribute(allow("unused_imports")) val AllowUnusedMut = Attribute(allow("unused_mut")) val AllowUnusedVariables = Attribute(allow("unused_variables")) @@ -481,11 +517,13 @@ class Attribute(val inner: Writable) { val DenyMissingDocs = Attribute(deny("missing_docs")) val DocHidden = Attribute(doc("hidden")) val DocInline = Attribute(doc("inline")) + val NoImplicitPrelude = Attribute("no_implicit_prelude") fun shouldPanic(expectedMessage: String) = Attribute(macroWithArgs("should_panic", "expected = ${expectedMessage.dq()}")) val Test = Attribute("test") val TokioTest = Attribute(RuntimeType.Tokio.resolve("test").writable) + val AwsSdkUnstableAttribute = Attribute(cfg("aws_sdk_unstable")) /** * [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute) @@ -514,10 +552,12 @@ class Attribute(val inner: Writable) { } fun all(vararg attrMacros: Writable): Writable = macroWithArgs("all", *attrMacros) + fun cfgAttr(vararg attrMacros: Writable): Writable = macroWithArgs("cfg_attr", *attrMacros) fun allow(lints: Collection): Writable = macroWithArgs("allow", *lints.toTypedArray()) fun allow(vararg lints: String): Writable = macroWithArgs("allow", *lints) fun deny(vararg lints: String): Writable = macroWithArgs("deny", *lints) + fun serde(vararg lints: String): Writable = macroWithArgs("serde", *lints) fun any(vararg attrMacros: Writable): Writable = macroWithArgs("any", *attrMacros) fun cfg(vararg attrMacros: Writable): Writable = macroWithArgs("cfg", *attrMacros) fun cfg(vararg attrMacros: String): Writable = macroWithArgs("cfg", *attrMacros) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt index 7fd296db83..dd17595c5f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt @@ -26,6 +26,7 @@ import software.amazon.smithy.model.traits.DocumentationTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.deprecated import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.ValueExpression import software.amazon.smithy.rust.codegen.core.smithy.rustType @@ -87,7 +88,9 @@ fun > T.withBlockTemplate( block: T.() -> Unit, ): T { return withTemplate(textBeforeNewLine, ctx) { header -> - conditionalBlock(header, textAfterNewLine, conditional = true, block = block) + withTemplate(textAfterNewLine, ctx) { tail -> + conditionalBlock(header, tail, conditional = true, block = block) + } } } @@ -140,6 +143,21 @@ fun > T.conditionalBlock( return this } +fun RustWriter.conditionalBlockTemplate( + textBeforeNewLine: String, + textAfterNewLine: String, + conditional: Boolean = true, + vararg args: Pair, + block: RustWriter.() -> Unit, +): RustWriter { + withTemplate(textBeforeNewLine.trim(), args) { beforeNewLine -> + withTemplate(textAfterNewLine.trim(), args) { afterNewLine -> + conditionalBlock(beforeNewLine, afterNewLine, conditional = conditional, block = block) + } + } + return this +} + /** * Convenience wrapper that tells Intellij that the contents of this block are Rust */ @@ -162,7 +180,11 @@ fun RustWriter.rustInline( /* rewrite #{foo} to #{foo:T} (the smithy template format) */ private fun transformTemplate(template: String, scope: Array>, trim: Boolean = true): String { - check(scope.distinctBy { it.first.lowercase() }.size == scope.size) { "Duplicate cased keys not supported" } + check( + scope.distinctBy { + it.first.lowercase() + }.size == scope.distinctBy { it.first }.size, + ) { "Duplicate cased keys not supported" } val output = template.replace(Regex("""#\{([a-zA-Z_0-9]+)(:\w)?\}""")) { matchResult -> val keyName = matchResult.groupValues[1] val templateType = matchResult.groupValues[2].ifEmpty { ":T" } @@ -448,6 +470,7 @@ class RustWriter private constructor( val devDependenciesOnly: Boolean = false, ) : SymbolWriter(UseDeclarations(namespace)) { + companion object { fun root() = forModule(null) fun forModule(module: String?): RustWriter = if (module == null) { @@ -469,10 +492,21 @@ class RustWriter private constructor( devDependenciesOnly = true, ) + fileName == "package.json" -> rawWriter(fileName, debugMode = debugMode) + fileName == "stubgen.sh" -> rawWriter(fileName, debugMode = debugMode) else -> RustWriter(fileName, namespace, debugMode = debugMode) } } + fun toml(fileName: String, debugMode: Boolean = false): RustWriter = + RustWriter( + fileName, + namespace = "ignore", + commentCharacter = "#", + printWarning = false, + debugMode = debugMode, + ) + private fun rawWriter(fileName: String, debugMode: Boolean): RustWriter = RustWriter( fileName, @@ -496,6 +530,8 @@ class RustWriter private constructor( return super.write(content, *args) } + fun dirty() = super.toString().isNotBlank() || preamble.isNotEmpty() + /** Helper function to determine if a stack frame is relevant for debug purposes */ private fun StackTraceElement.isRelevant(): Boolean { if (this.className.contains("AbstractCodeWriter") || this.className.startsWith("java.lang")) { @@ -514,7 +550,7 @@ class RustWriter private constructor( init { expressionStart = '#' if (filename.endsWith(".rs")) { - require(namespace.startsWith("crate") || filename.startsWith("tests/")) { + require(namespace.startsWith("crate") || filename.startsWith("tests/") || filename == "build.rs") { "We can only write into files in the crate (got $namespace)" } } @@ -605,7 +641,7 @@ class RustWriter private constructor( when { member.isOptional() -> { val innerValue = ValueExpression.Reference(safeName("inner")) - rustBlock("if let Some(${innerValue.name}) = ${value.asRef()}") { + rustBlockTemplate("if let #{Some}(${innerValue.name}) = ${value.asRef()}", *preludeScope) { block(innerValue) } } @@ -692,7 +728,8 @@ class RustWriter private constructor( override fun toString(): String { val contents = super.toString() val preheader = if (preamble.isNotEmpty()) { - val prewriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) + val prewriter = + RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) preamble.forEach { it(prewriter) } prewriter.toString() } else { @@ -738,7 +775,8 @@ class RustWriter private constructor( @Suppress("UNCHECKED_CAST") val func = t as? Writable ?: throw CodegenException("RustWriteableInjector.apply choked on non-function t ($t)") - val innerWriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) + val innerWriter = + RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) func(innerWriter) innerWriter.dependencies.forEach { addDependencyTestAware(it) } return innerWriter.toString().trimEnd() @@ -771,7 +809,8 @@ class RustWriter private constructor( @Suppress("UNCHECKED_CAST") val func = t as? Writable ?: throw CodegenException("Invalid function type (expected writable) ($t)") - val innerWriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) + val innerWriter = + RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) func(innerWriter) innerWriter.dependencies.forEach { addDependencyTestAware(it) } return innerWriter.toString().trimEnd() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegator.kt index 157a6f2539..5ae40c3574 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.codegen.core.WriterDelegator import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.DEV_ONLY_FEATURES import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency @@ -298,7 +299,9 @@ internal fun List.mergeIdenticalTestDependencies(): List + dep.scope == DependencyScope.Dev && + DEV_ONLY_FEATURES.none { devOnly -> dep.features.contains(devOnly) } && + compileDeps.contains(dep.copy(scope = DependencyScope.Compile)) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt index 6eeab26d65..fdb6e35a83 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustType -import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait @@ -48,15 +47,15 @@ class EventStreamSymbolProvider( } else { symbolForEventStreamError(unionShape) } - val errorFmt = error.rustType().render(fullyQualified = true) - val innerFmt = initial.rustType().stripOuter().render(fullyQualified = true) + val errorT = error.rustType() + val innerT = initial.rustType().stripOuter() val isSender = (shape.isInputEventStream(model) && target == CodegenTarget.CLIENT) || (shape.isOutputEventStream(model) && target == CodegenTarget.SERVER) val outer = when (isSender) { - true -> "EventStreamSender<$innerFmt, $errorFmt>" - else -> "Receiver<$innerFmt, $errorFmt>" + true -> RuntimeType.eventStreamSender(runtimeConfig).toSymbol().rustType() + else -> RuntimeType.eventStreamReceiver(runtimeConfig).toSymbol().rustType() } - val rustType = RustType.Opaque(outer, "aws_smithy_http::event_stream") + val rustType = RustType.Application(outer, listOf(innerT, errorT)) return initial.toBuilder() .name(rustType.name) .rustType(rustType) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 490aab7c25..e24edc7f86 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -96,6 +96,8 @@ data class RuntimeConfig( val crateSrcPrefix: String = cratePrefix.replace("-", "_") + fun runtimeCratesPath(): String? = runtimeCrateLocation.path + fun smithyRuntimeCrate( runtimeCrateName: String, optional: Boolean = false, @@ -191,14 +193,67 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) * The companion object contains commonly used RuntimeTypes */ companion object { + /** + * Scope that contains all Rust prelude types, but not macros or functions. + * + * Prelude docs: https://doc.rust-lang.org/std/prelude/index.html#prelude-contents + */ + val preludeScope by lazy { + arrayOf( + // Rust 1.0 + "Copy" to std.resolve("marker::Copy"), + "Send" to Send, + "Sized" to std.resolve("marker::Sized"), + "Sync" to Sync, + "Unpin" to std.resolve("marker::Unpin"), + "Drop" to std.resolve("ops::Drop"), + "Fn" to std.resolve("ops::Fn"), + "FnMut" to std.resolve("ops::FnMut"), + "FnOnce" to std.resolve("ops::FnOnce"), + "Box" to Box, + "ToOwned" to std.resolve("borrow::ToOwned"), + "Clone" to Clone, + "PartialEq" to std.resolve("cmp::PartialEq"), + "PartialOrd" to std.resolve("cmp::PartialOrd"), + "Eq" to Eq, + "Ord" to Ord, + "AsRef" to AsRef, + "AsMut" to std.resolve("convert::AsMut"), + "Into" to Into, + "From" to From, + "Default" to Default, + "Iterator" to std.resolve("iter::Iterator"), + "Extend" to std.resolve("iter::Extend"), + "IntoIterator" to std.resolve("iter::IntoIterator"), + "DoubleEndedIterator" to std.resolve("iter::DoubleEndedIterator"), + "ExactSizeIterator" to std.resolve("iter::ExactSizeIterator"), + "Option" to Option, + "Some" to Option.resolve("Some"), + "None" to Option.resolve("None"), + "Result" to std.resolve("result::Result"), + "Ok" to std.resolve("result::Result::Ok"), + "Err" to std.resolve("result::Result::Err"), + "String" to String, + "ToString" to std.resolve("string::ToString"), + "Vec" to Vec, + + // 2021 Edition + "TryFrom" to std.resolve("convert::TryFrom"), + "TryInto" to std.resolve("convert::TryInto"), + "FromIterator" to std.resolve("iter::FromIterator"), + ) + } + // stdlib types - val std = RuntimeType("std") + val std = RuntimeType("::std") val stdCmp = std.resolve("cmp") val stdFmt = std.resolve("fmt") val stdConvert = std.resolve("convert") + val Arc = std.resolve("sync::Arc") val AsRef = stdConvert.resolve("AsRef") - val ByteSlab = std.resolve("vec::Vec") + val Bool = std.resolve("primitive::bool") val Box = std.resolve("boxed::Box") + val ByteSlab = std.resolve("vec::Vec") val Clone = std.resolve("clone::Clone") val Cow = std.resolve("borrow::Cow") val Debug = stdFmt.resolve("Debug") @@ -208,15 +263,18 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) val From = stdConvert.resolve("From") val Hash = std.resolve("hash::Hash") val HashMap = std.resolve("collections::HashMap") - val Ord = stdCmp.resolve("Ord") + val Into = stdConvert.resolve("Into") val Option = std.resolve("option::Option") + val Ord = stdCmp.resolve("Ord") val PartialEq = stdCmp.resolve("PartialEq") val PartialOrd = stdCmp.resolve("PartialOrd") val Phantom = std.resolve("marker::PhantomData") + val Send = std.resolve("marker::Send") val StdError = std.resolve("error::Error") val String = std.resolve("string::String") - val Bool = std.resolve("primitive::bool") + val Sync = std.resolve("marker::Sync") val TryFrom = stdConvert.resolve("TryFrom") + val U64 = std.resolve("primitive::u64") val Vec = std.resolve("vec::Vec") // external cargo dependency types @@ -244,12 +302,18 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) val ConstrainedTrait = RuntimeType("crate::constrained::Constrained", InlineDependency.constrained()) val MaybeConstrained = RuntimeType("crate::constrained::MaybeConstrained", InlineDependency.constrained()) + // serde types. Gated behind `CfgUnstable`. + val Serde = CargoDependency.Serde.toType() + val SerdeSerialize = Serde.resolve("Serialize") + val SerdeDeserialize = Serde.resolve("Deserialize") + // smithy runtime types fun smithyAsync(runtimeConfig: RuntimeConfig) = CargoDependency.smithyAsync(runtimeConfig).toType() fun smithyChecksums(runtimeConfig: RuntimeConfig) = CargoDependency.smithyChecksums(runtimeConfig).toType() fun smithyClient(runtimeConfig: RuntimeConfig) = CargoDependency.smithyClient(runtimeConfig).toType() fun smithyClientTestUtil(runtimeConfig: RuntimeConfig) = CargoDependency.smithyClient(runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev).toType() + fun smithyEventStream(runtimeConfig: RuntimeConfig) = CargoDependency.smithyEventStream(runtimeConfig).toType() fun smithyHttp(runtimeConfig: RuntimeConfig) = CargoDependency.smithyHttp(runtimeConfig).toType() fun smithyHttpAuth(runtimeConfig: RuntimeConfig) = CargoDependency.smithyHttpAuth(runtimeConfig).toType() @@ -270,19 +334,71 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun base64Encode(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("base64::encode") + fun configBag(runtimeConfig: RuntimeConfig): RuntimeType = + smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag") + fun runtimeComponents(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_components::RuntimeComponents") + fun runtimeComponentsBuilder(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_components::RuntimeComponentsBuilder") + fun runtimePlugins(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugins") + fun runtimePlugin(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugin") + fun sharedRuntimePlugin(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::SharedRuntimePlugin") + fun boxError(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("box_error::BoxError") + fun interceptor(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::Interceptor") + fun interceptorContext(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::InterceptorContext") + fun sharedInterceptor(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::SharedInterceptor") + + fun afterDeserializationInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::AfterDeserializationInterceptorContextRef") + fun beforeSerializationInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeSerializationInterceptorContextRef") + fun beforeSerializationInterceptorContextMut(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeSerializationInterceptorContextMut") + fun beforeDeserializationInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeDeserializationInterceptorContextRef") + fun beforeDeserializationInterceptorContextMut(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeDeserializationInterceptorContextMut") + fun beforeTransmitInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeTransmitInterceptorContextRef") + fun beforeTransmitInterceptorContextMut(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeTransmitInterceptorContextMut") + fun finalizerInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::FinalizerInterceptorContextRef") + fun finalizerInterceptorContextMut(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::FinalizerInterceptorContextMut") + fun blob(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("Blob") fun byteStream(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("byte_stream::ByteStream") fun classifyRetry(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("retry::ClassifyRetry") fun dateTime(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("DateTime") fun document(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("Document") + fun format(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("date_time::Format") fun retryErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") - fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") + fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = + smithyHttp(runtimeConfig).resolve("event_stream::Receiver") + + fun eventStreamSender(runtimeConfig: RuntimeConfig): RuntimeType = + smithyHttp(runtimeConfig).resolve("event_stream::EventStreamSender") + fun errorMetadata(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ErrorMetadata") - fun errorMetadataBuilder(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::metadata::Builder") - fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::metadata::ProvideErrorMetadata") + fun errorMetadataBuilder(runtimeConfig: RuntimeConfig) = + smithyTypes(runtimeConfig).resolve("error::metadata::Builder") + + fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) = + smithyTypes(runtimeConfig).resolve("error::metadata::ProvideErrorMetadata") + fun unhandledError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Unhandled") fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig)) - fun awsQueryCompatibleErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.awsQueryCompatibleErrors(runtimeConfig)) + fun awsQueryCompatibleErrors(runtimeConfig: RuntimeConfig) = + forInlineDependency(InlineDependency.awsQueryCompatibleErrors(runtimeConfig)) + fun labelFormat(runtimeConfig: RuntimeConfig, func: String) = smithyHttp(runtimeConfig).resolve("label::$func") fun operation(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("operation::Operation") fun operationModule(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("operation") @@ -338,7 +454,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun captureRequest(runtimeConfig: RuntimeConfig) = CargoDependency.smithyClientTestUtil(runtimeConfig).toType().resolve("test_connection::capture_request") - fun forInlineDependency(inlineDependency: InlineDependency) = RuntimeType("crate::${inlineDependency.name}", inlineDependency) + fun forInlineDependency(inlineDependency: InlineDependency) = + RuntimeType("crate::${inlineDependency.name}", inlineDependency) fun forInlineFun(name: String, module: RustModule, func: Writable) = RuntimeType( "${module.fullyQualifiedPath()}::$name", @@ -355,6 +472,7 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.unwrappedXmlErrors(runtimeConfig)) - val IdempotencyToken by lazy { forInlineDependency(InlineDependency.idempotencyToken()) } + fun idempotencyToken(runtimeConfig: RuntimeConfig) = + forInlineDependency(InlineDependency.idempotencyToken(runtimeConfig)) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt index 880ac0510a..964f2046dd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt @@ -20,7 +20,6 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.SensitiveTrait -import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.Visibility @@ -99,19 +98,7 @@ class BaseSymbolMetadataProvider( override fun memberMeta(memberShape: MemberShape): RustMetadata = when (val container = model.expectShape(memberShape.container)) { - is StructureShape -> { - // TODO(https://github.com/awslabs/smithy-rs/issues/943): Once streaming accessors are usable, - // then also make streaming members `#[doc(hidden)]` - if (memberShape.getMemberTrait(model, StreamingTrait::class.java).isPresent) { - RustMetadata(visibility = Visibility.PUBLIC) - } else { - RustMetadata( - // At some point, visibility _may_ be made `PRIVATE`, so make these `#[doc(hidden)]` for now. - visibility = Visibility.PUBLIC, - additionalAttributes = listOf(Attribute.DocHidden), - ) - } - } + is StructureShape -> RustMetadata(visibility = Visibility.PUBLIC) is UnionShape, is CollectionShape, is MapShape -> RustMetadata(visibility = Visibility.PUBLIC) @@ -137,13 +124,13 @@ class BaseSymbolMetadataProvider( // Only the server subproject uses these, so we provide a sane and conservative default implementation here so that // the rest of symbol metadata providers can just delegate to it. - private val defaultRustMetadata = RustMetadata(visibility = Visibility.PRIVATE) + private fun defaultRustMetadata() = RustMetadata(visibility = Visibility.PRIVATE) - override fun listMeta(listShape: ListShape) = defaultRustMetadata - override fun mapMeta(mapShape: MapShape) = defaultRustMetadata - override fun stringMeta(stringShape: StringShape) = defaultRustMetadata - override fun numberMeta(numberShape: NumberShape) = defaultRustMetadata - override fun blobMeta(blobShape: BlobShape) = defaultRustMetadata + override fun listMeta(listShape: ListShape) = defaultRustMetadata() + override fun mapMeta(mapShape: MapShape) = defaultRustMetadata() + override fun stringMeta(stringShape: StringShape) = defaultRustMetadata() + override fun numberMeta(numberShape: NumberShape) = defaultRustMetadata() + override fun blobMeta(blobShape: BlobShape) = defaultRustMetadata() } private const val META_KEY = "meta" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt index ec49a97613..301ee11da8 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt @@ -58,6 +58,7 @@ private fun pubUseTypesThatShouldBeExported(codegenContext: CodegenContext, mode listOf( PubUseType(RuntimeType.blob(runtimeConfig), ::hasBlobs), PubUseType(RuntimeType.dateTime(runtimeConfig), ::hasDateTimes), + PubUseType(RuntimeType.format(runtimeConfig), ::hasDateTimes, "DateTimeFormat"), ) + RuntimeType.smithyHttp(runtimeConfig).let { http -> listOf( PubUseType(http.resolve("byte_stream::ByteStream"), ::hasStreamingOperations), diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt index 1df3fb7dd3..c91d705ac3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt @@ -23,7 +23,7 @@ import java.util.logging.Logger /** * Represents the bare minimum for codegen plugin customization. */ -interface CoreCodegenDecorator { +interface CoreCodegenDecorator { /** * The name of this decorator, used for logging and debug information */ @@ -43,7 +43,7 @@ interface CoreCodegenDecorator { /** * Hook to transform the Smithy model before codegen takes place. */ - fun transformModel(service: ServiceShape, model: Model): Model = model + fun transformModel(service: ServiceShape, model: Model, settings: CodegenSettings): Model = model /** * Hook to add additional modules to the generated crate. @@ -114,9 +114,9 @@ interface CoreCodegenDecorator { /** * Implementations for combining decorators for the core customizations. */ -abstract class CombinedCoreCodegenDecorator>( +abstract class CombinedCoreCodegenDecorator>( decorators: List, -) : CoreCodegenDecorator { +) : CoreCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } final override fun crateManifestCustomizations(codegenContext: CodegenContext): ManifestCustomizations = @@ -128,9 +128,9 @@ abstract class CombinedCoreCodegenDecorator - decorator.transformModel(otherModel.expectShape(service.id, ServiceShape::class.java), otherModel) + decorator.transformModel(otherModel.expectShape(service.id, ServiceShape::class.java), otherModel, settings) } final override fun moduleDocumentationCustomization( @@ -209,7 +209,7 @@ abstract class CombinedCoreCodegenDecorator> decoratorsFromClasspath( + protected fun > decoratorsFromClasspath( context: PluginContext, decoratorClass: Class, logger: Logger, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/Customization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/Customization.kt index d563349867..c174c81d95 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/Customization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/Customization.kt @@ -60,3 +60,18 @@ fun RustWriter.writeCustomizations(customizations: List RustWriter.writeCustomizationsOrElse( + customizations: List>, + section: T, + orElse: Writable, +) { + val test = RustWriter.root() + test.writeCustomizations(customizations, section) + if (test.dirty()) { + writeCustomizations(customizations, section) + } else { + orElse(this) + } +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt deleted file mode 100644 index e11936c704..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.core.smithy.customize - -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol - -sealed class OperationSection(name: String) : Section(name) { - abstract val customizations: List - - /** Write custom code into the `impl` block of this operation */ - data class OperationImplBlock(override val customizations: List) : - OperationSection("OperationImplBlock") - - /** Write additional functions inside the Input's impl block */ - data class InputImpl( - override val customizations: List, - val operationShape: OperationShape, - val inputShape: StructureShape, - val protocol: Protocol, - ) : OperationSection("InputImpl") - - data class MutateInput( - override val customizations: List, - val input: String, - val config: String, - ) : OperationSection("MutateInput") - - /** Write custom code into the block that builds an operation - * - * [request]: Name of the variable holding the `aws_smithy_http::Request` - * [config]: Name of the variable holding the service config. - * - * */ - data class MutateRequest( - override val customizations: List, - val request: String, - val config: String, - ) : OperationSection("Feature") - - data class FinalizeOperation( - override val customizations: List, - val operation: String, - val config: String, - ) : OperationSection("Finalize") - - data class MutateOutput( - override val customizations: List, - val operationShape: OperationShape, - /** Name of the response headers map (for referring to it in Rust code) */ - val responseHeadersName: String, - ) : OperationSection("MutateOutput") - - /** - * Allows for adding additional properties to the `extras` field on the - * `aws_smithy_types::error::ErrorMetadata`. - */ - data class PopulateErrorMetadataExtras( - override val customizations: List, - /** Name of the generic error builder (for referring to it in Rust code) */ - val builderName: String, - /** Name of the response status (for referring to it in Rust code) */ - val responseStatusName: String, - /** Name of the response headers map (for referring to it in Rust code) */ - val responseHeadersName: String, - ) : OperationSection("PopulateErrorMetadataExtras") - - /** - * Hook to add custom code right before the response is parsed. - */ - data class BeforeParseResponse( - override val customizations: List, - val responseName: String, - ) : OperationSection("BeforeParseResponse") -} - -abstract class OperationCustomization : NamedCustomization() { - open fun retryType(): RuntimeType? = null - - /** - * Does `make_operation` consume the self parameter? - * - * This is required for things like idempotency tokens where the operation can only be sent once - * and an idempotency token will mutate the request. - */ - open fun consumesSelf(): Boolean = false - - /** - * Does `make_operation` mutate the self parameter? - */ - open fun mutSelf(): Boolean = false -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index 16cb28b803..e86061c4c3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -17,13 +17,14 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asArgument import software.amazon.smithy.rust.codegen.core.rustlang.asOptional -import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.deprecatedShape import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlock @@ -31,6 +32,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization @@ -91,6 +93,9 @@ class OperationBuildError(private val runtimeConfig: RuntimeConfig) { // Setter names will never hit a reserved word and therefore never need escaping. fun MemberShape.setterName() = "set_${this.memberName.toSnakeCase()}" +// Getter names will never hit a reserved word and therefore never need escaping. +fun MemberShape.getterName() = "get_${this.memberName.toSnakeCase()}" + class BuilderGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, @@ -126,7 +131,6 @@ class BuilderGenerator( private val runtimeConfig = symbolProvider.config.runtimeConfig private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) - private val builderSymbol = symbolProvider.symbolForBuilder(shape) private val metadata = structureSymbol.expectRustMetadata() // Filter out any derive that isn't Debug, PartialEq, or Clone. Then add a Default derive @@ -147,12 +151,12 @@ class BuilderGenerator( val fallibleBuilder = hasFallibleBuilder(shape, symbolProvider) val outputSymbol = symbolProvider.toSymbol(shape) val returnType = when (fallibleBuilder) { - true -> "Result<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" + true -> "#{Result}<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" false -> implBlockWriter.format(outputSymbol) } implBlockWriter.docs("Consumes the builder and constructs a #D.", outputSymbol) - implBlockWriter.rustBlock("pub fn build(self) -> $returnType") { - conditionalBlock("Ok(", ")", conditional = fallibleBuilder) { + implBlockWriter.rustBlockTemplate("pub fn build(self) -> $returnType", *preludeScope) { + conditionalBlockTemplate("#{Ok}(", ")", conditional = fallibleBuilder, *preludeScope) { // If a wrapper is specified, use the `::new` associated function to construct the wrapper coreBuilder(this) } @@ -182,7 +186,7 @@ class BuilderGenerator( writer.documentShape(member, model) writer.deprecatedShape(member) writer.rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { - write("self.$memberName = Some(${input.value});") + rustTemplate("self.$memberName = #{Some}(${input.value});", *preludeScope) write("self") } } @@ -209,6 +213,28 @@ class BuilderGenerator( } } + /** + * Render a `get_foo` method. This is useful as a target for code generation, because the argument type + * is the same as the resulting member type, and is always optional. + */ + private fun renderBuilderMemberGetterFn( + writer: RustWriter, + outerType: RustType, + member: MemberShape, + memberName: String, + ) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1302): This `asOptional()` call is superfluous except in + // the case where the shape is a `@streaming` blob, because [StreamingTraitSymbolProvider] always generates + // a non `Option`al target type: in all other cases the client generates `Option`al types. + val inputType = outerType.asOptional() + + writer.documentShape(member, model) + writer.deprecatedShape(member) + writer.rustBlock("pub fn ${member.getterName()}(&self) -> &${inputType.render(true)}") { + rust("&self.$memberName") + } + } + private fun renderBuilder(writer: RustWriter) { writer.docs("A builder for #D.", structureSymbol) metadata.additionalAttributes.render(writer) @@ -239,6 +265,7 @@ class BuilderGenerator( } renderBuilderMemberSetterFn(this, outerType, member, memberName) + renderBuilderMemberGetterFn(this, outerType, member, memberName) } writeCustomizations(customizations, BuilderSection.AdditionalMethods(shape)) renderBuildFn(this) @@ -273,13 +300,14 @@ class BuilderGenerator( val input = coreType.member.asArgument("input") rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { - rust( + rustTemplate( """ let mut v = self.$memberName.unwrap_or_default(); v.push(${input.value}); - self.$memberName = Some(v); + self.$memberName = #{Some}(v); self """, + *preludeScope, ) } } @@ -297,13 +325,14 @@ class BuilderGenerator( rustBlock( "pub fn $memberName(mut self, ${k.argument}, ${v.argument}) -> Self", ) { - rust( + rustTemplate( """ let mut hash_map = self.$memberName.unwrap_or_default(); hash_map.insert(${k.value}, ${v.value}); - self.$memberName = Some(hash_map); + self.$memberName = #{Some}(hash_map); self """, + *preludeScope, ) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt index cceced878b..b5eca73acd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt @@ -43,12 +43,36 @@ typealias ManifestCustomizations = Map * Generates the crate manifest Cargo.toml file. */ class CargoTomlGenerator( - private val settings: CoreRustSettings, + private val moduleName: String, + private val moduleVersion: String, + private val moduleAuthors: List, + private val moduleDescription: String?, + private val moduleLicense: String?, + private val moduleRepository: String?, private val writer: RustWriter, - private val manifestCustomizations: ManifestCustomizations, - private val dependencies: List, - private val features: List, + private val manifestCustomizations: ManifestCustomizations = emptyMap(), + private val dependencies: List = emptyList(), + private val features: List = emptyList(), ) { + constructor( + settings: CoreRustSettings, + writer: RustWriter, + manifestCustomizations: ManifestCustomizations, + dependencies: List, + features: List, + ) : this( + settings.moduleName, + settings.moduleVersion, + settings.moduleAuthors, + settings.moduleDescription, + settings.license, + settings.moduleRepository, + writer, + manifestCustomizations, + dependencies, + features, + ) + fun render() { val cargoFeatures = features.map { it.name to it.deps }.toMutableList() if (features.isNotEmpty()) { @@ -57,13 +81,13 @@ class CargoTomlGenerator( val cargoToml = mapOf( "package" to listOfNotNull( - "name" to settings.moduleName, - "version" to settings.moduleVersion, - "authors" to settings.moduleAuthors, - settings.moduleDescription?.let { "description" to it }, + "name" to moduleName, + "version" to moduleVersion, + "authors" to moduleAuthors, + moduleDescription?.let { "description" to it }, "edition" to "2021", - "license" to settings.license, - "repository" to settings.moduleRepository, + "license" to moduleLicense, + "repository" to moduleRepository, "metadata" to listOfNotNull( "smithy" to listOfNotNull( "codegen-version" to Version.fullVersion(), @@ -72,6 +96,8 @@ class CargoTomlGenerator( ).toMap(), "dependencies" to dependencies.filter { it.scope == DependencyScope.Compile } .associate { it.name to it.toMap() }, + "build-dependencies" to dependencies.filter { it.scope == DependencyScope.Build } + .associate { it.name to it.toMap() }, "dev-dependencies" to dependencies.filter { it.scope == DependencyScope.Dev } .associate { it.name to it.toMap() }, "features" to cargoFeatures.toMap(), diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt index 379a6982da..a84820ad26 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt @@ -27,6 +27,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.MaybeRenamed import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom @@ -208,14 +209,15 @@ open class EnumGenerator( } }, ) - rust( + rustTemplate( """ - impl AsRef for ${context.enumName} { + impl #{AsRef} for ${context.enumName} { fn as_ref(&self) -> &str { self.as_str() } } """, + *preludeScope, ) } @@ -238,8 +240,7 @@ open class EnumGenerator( } } """, - "From" to RuntimeType.From, - "AsRef" to RuntimeType.AsRef, + *preludeScope, ) } @@ -295,7 +296,7 @@ open class EnumGenerator( """ impl #{Debug} for ${context.enumName} { fn fmt(&self, f: &mut #{StdFmt}::Formatter<'_>) -> #{StdFmt}::Result { - write!(f, $REDACTION) + ::std::write!(f, $REDACTION) } } """, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt index e77f942a29..eed196e489 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt @@ -37,7 +37,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.escape import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock @@ -47,6 +47,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section @@ -204,21 +205,23 @@ open class Instantiator( check(symbol.isOptional()) { "A null node was provided for $memberShape but the symbol was not optional. This is invalid input data." } - writer.rust("None") + writer.rustTemplate("#{None}", *preludeScope) } else { // Structure builder setters for structure shape members _always_ take in `Option`. // Other aggregate shapes' members are optional only when their symbol is. - writer.conditionalBlock( - "Some(", + writer.conditionalBlockTemplate( + "#{Some}(", ")", // The conditions are not commutative: note client builders always take in `Option`. conditional = symbol.isOptional() || (model.expectShape(memberShape.container) is StructureShape && builderKindBehavior.doesSetterTakeInOption(memberShape)), + *preludeScope, ) { - writer.conditionalBlock( - "Box::new(", + writer.conditionalBlockTemplate( + "#{Box}::new(", ")", conditional = symbol.rustType().stripOuter() is RustType.Box, + *preludeScope, ) { render( this, @@ -330,6 +333,23 @@ open class Instantiator( * ``` */ private fun renderStructure(writer: RustWriter, shape: StructureShape, data: ObjectNode, headers: Map, ctx: Ctx) { + writer.rust("#T::builder()", symbolProvider.toSymbol(shape)) + + renderStructureMembers(writer, shape, data, headers, ctx) + + writer.rust(".build()") + if (builderKindBehavior.hasFallibleBuilder(shape)) { + writer.rust(".unwrap()") + } + } + + protected fun renderStructureMembers( + writer: RustWriter, + shape: StructureShape, + data: ObjectNode, + headers: Map, + ctx: Ctx, + ) { fun renderMemberHelper(memberShape: MemberShape, value: Node) { val setterName = builderKindBehavior.setterName(memberShape) writer.withBlock(".$setterName(", ")") { @@ -337,7 +357,6 @@ open class Instantiator( } } - writer.rust("#T::builder()", symbolProvider.toSymbol(shape)) if (defaultsForRequiredFields) { shape.allMembers.entries .filter { (name, memberShape) -> @@ -371,11 +390,6 @@ open class Instantiator( ?.let { renderMemberHelper(it.value, fillDefaultValue(model.expectShape(it.value.target))) } - - writer.rust(".build()") - if (builderKindBehavior.hasFallibleBuilder(shape)) { - writer.rust(".unwrap()") - } } /** diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TypeConversionGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TypeConversionGenerator.kt deleted file mode 100644 index b79200f952..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TypeConversionGenerator.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.core.smithy.generators - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.BlobShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.rustType - -/* - * Utility class used to force casting a non primitive type into one overriden by a new symbol provider, - * by explicitly calling `from()` or into(). - * - * For example we use this in the server Python implementation, where we override types like [Blob] and [DateTime] - * with wrappers compatible with Python, without touching the original implementation coming from `aws-smithy-types`. - */ -class TypeConversionGenerator(private val model: Model, private val symbolProvider: RustSymbolProvider, private val runtimeConfig: RuntimeConfig) { - private fun findOldSymbol(shape: Shape): Symbol { - return when (shape) { - is BlobShape -> RuntimeType.blob(runtimeConfig).toSymbol() - is TimestampShape -> RuntimeType.dateTime(runtimeConfig).toSymbol() - else -> symbolProvider.toSymbol(shape) - } - } - - fun convertViaFrom(shape: Shape): Writable = - writable { - val oldSymbol = findOldSymbol(shape) - val newSymbol = symbolProvider.toSymbol(shape) - if (oldSymbol.rustType() != newSymbol.rustType()) { - rust(".map($newSymbol::from)") - } - } - - fun convertViaInto(shape: Shape): Writable = - writable { - val oldSymbol = findOldSymbol(shape) - val newSymbol = symbolProvider.toSymbol(shape) - if (oldSymbol.rustType() != newSymbol.rustType()) { - rust(".into()") - } - } -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt index 39b191ba96..3f7927d728 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt @@ -19,9 +19,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom import software.amazon.smithy.rust.codegen.core.smithy.rustType @@ -134,7 +136,7 @@ open class UnionGenerator( """ impl #{Debug} for ${unionSymbol.name} { fn fmt(&self, f: &mut #{StdFmt}::Formatter<'_>) -> #{StdFmt}::Result { - write!(f, $REDACTION) + ::std::write!(f, $REDACTION) } } """, @@ -197,8 +199,11 @@ private fun RustWriter.renderAsVariant( "/// Tries to convert the enum instance into [`$variantName`], extracting the inner `()`.", ) rust("/// Returns `Err(&Self)` if it can't be converted.") - rustBlock("pub fn as_$funcNamePart(&self) -> std::result::Result<(), &Self>") { - rust("if let ${unionSymbol.name}::$variantName = &self { Ok(()) } else { Err(self) }") + rustBlockTemplate("pub fn as_$funcNamePart(&self) -> #{Result}<(), &Self>", *preludeScope) { + rustTemplate( + "if let ${unionSymbol.name}::$variantName = &self { #{Ok}(()) } else { #{Err}(self) }", + *preludeScope, + ) } } else { val memberSymbol = symbolProvider.toSymbol(member) @@ -209,8 +214,11 @@ private fun RustWriter.renderAsVariant( targetSymbol, ) rust("/// Returns `Err(&Self)` if it can't be converted.") - rustBlock("pub fn as_$funcNamePart(&self) -> std::result::Result<&${memberSymbol.rustType().render()}, &Self>") { - rust("if let ${unionSymbol.name}::$variantName(val) = &self { Ok(val) } else { Err(self) }") + rustBlockTemplate("pub fn as_$funcNamePart(&self) -> #{Result}<&${memberSymbol.rustType().render()}, &Self>", *preludeScope) { + rustTemplate( + "if let ${unionSymbol.name}::$variantName(val) = &self { #{Ok}(val) } else { #{Err}(self) }", + *preludeScope, + ) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt index 692bf32aab..049933bc45 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt @@ -156,13 +156,13 @@ class ErrorImplGenerator( val errorDesc = symbol.name.letIf(symbol.name != shape.id.name) { symbolName -> "$symbolName [${shape.id.name}]" } - write("write!(f, ${errorDesc.dq()})?;") + write("::std::write!(f, ${errorDesc.dq()})?;") messageShape?.let { if (it.shouldRedact(model)) { - write("""write!(f, ": {}", $REDACTION)?;""") + write("""::std::write!(f, ": {}", $REDACTION)?;""") } else { ifSet(it, symbolProvider.toSymbol(it), ValueExpression.Reference("&self.message")) { field -> - write("""write!(f, ": {}", ${field.asRef()})?;""") + write("""::std::write!(f, ": {}", ${field.asRef()})?;""") } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index d53e09df68..7268dfd12a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -30,6 +30,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asOptional +import software.amazon.smithy.rust.codegen.core.rustlang.qualifiedName import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock @@ -87,6 +88,9 @@ sealed class HttpBindingSection(name: String) : Section(name) { data class AfterDeserializingIntoAHashMapOfHttpPrefixHeaders(val memberShape: MemberShape) : HttpBindingSection("AfterDeserializingIntoAHashMapOfHttpPrefixHeaders") + + data class AfterDeserializingIntoADateTimeOfHttpHeaders(val memberShape: MemberShape) : + HttpBindingSection("AfterDeserializingIntoADateTimeOfHttpHeaders") } typealias HttpBindingCustomization = NamedCustomization @@ -222,7 +226,7 @@ class HttpBindingGenerator( // Streaming unions are Event Streams and should be handled separately val target = model.expectShape(binding.member.target) if (target is UnionShape) { - bindEventStreamOutput(operationShape, target) + bindEventStreamOutput(operationShape, outputT, target) } else { deserializeStreamingBody(binding) } @@ -231,7 +235,7 @@ class HttpBindingGenerator( // The output needs to be Optional when deserializing the payload body or the caller signature // will not match. val outputT = symbolProvider.toSymbol(binding.member).makeOptional() - rustBlock("pub fn $fnName(body: &[u8]) -> std::result::Result<#T, #T>", outputT, errorSymbol) { + rustBlock("pub(crate) fn $fnName(body: &[u8]) -> std::result::Result<#T, #T>", outputT, errorSymbol) { deserializePayloadBody( binding, errorSymbol, @@ -243,22 +247,22 @@ class HttpBindingGenerator( } } - private fun RustWriter.bindEventStreamOutput(operationShape: OperationShape, targetShape: UnionShape) { + private fun RustWriter.bindEventStreamOutput(operationShape: OperationShape, outputT: Symbol, targetShape: UnionShape) { val unmarshallerConstructorFn = EventStreamUnmarshallerGenerator( protocol, codegenContext, operationShape, targetShape, ).render() + val receiver = outputT.rustType().qualifiedName() rustTemplate( """ let unmarshaller = #{unmarshallerConstructorFn}(); let body = std::mem::replace(body, #{SdkBody}::taken()); - Ok(#{Receiver}::new(unmarshaller, body)) + Ok($receiver::new(unmarshaller, body)) """, "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "unmarshallerConstructorFn" to unmarshallerConstructorFn, - "Receiver" to RuntimeType.eventStreamReceiver(runtimeConfig), ) } @@ -352,7 +356,7 @@ class HttpBindingGenerator( rustType to targetShape } val parsedValue = safeName() - if (coreType == dateTime) { + if (coreShape.isTimestampShape()) { val timestampFormat = index.determineTimestampFormat( memberShape, @@ -361,10 +365,14 @@ class HttpBindingGenerator( ) val timestampFormatType = RuntimeType.parseTimestampFormat(codegenTarget, runtimeConfig, timestampFormat) rust( - "let $parsedValue: Vec<${coreType.render()}> = #T::many_dates(headers, #T)?;", + "let $parsedValue: Vec<${coreType.render()}> = #T::many_dates(headers, #T)?", headerUtil, timestampFormatType, ) + for (customization in customizations) { + customization.section(HttpBindingSection.AfterDeserializingIntoADateTimeOfHttpHeaders(memberShape))(this) + } + rust(";") } else if (coreShape.isPrimitive()) { rust( "let $parsedValue = #T::read_many_primitive::<${coreType.render()}>(headers)?;", diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt index 4f410bf03a..b884a249bf 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt @@ -7,8 +7,9 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol + +/** Allows for additional context to be given to the payload generator from where it is being called */ +interface AdditionalPayloadContext /** * Payload Body Generator. @@ -31,32 +32,24 @@ interface ProtocolPayloadGenerator { * Most operations will use the HTTP payload as a reference, but for operations that will consume the entire stream * later,they will need to take ownership and different code needs to be generated. */ - fun payloadMetadata(operationShape: OperationShape): PayloadMetadata + fun payloadMetadata( + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext = object : AdditionalPayloadContext {}, + ): PayloadMetadata /** * Write the payload into [writer]. * - * [self] is the name of the variable binding for the Rust struct that is to be serialized into the payload. + * [shapeName] is the name of the variable binding for the Rust struct that is to be serialized into the payload. * * This should be an expression that returns bytes: * - a `Vec` for non-streaming operations; or * - a `ByteStream` for streaming operations. */ - fun generatePayload(writer: RustWriter, self: String, operationShape: OperationShape) -} - -/** - * Class providing scaffolding for HTTP based protocols that must build an HTTP request (headers / URL) and a body. - */ -abstract class ProtocolGenerator( - codegenContext: CodegenContext, - /** - * `Protocol` contains all protocol specific information. Each smithy protocol, e.g. RestJson, RestXml, etc. will - * have their own implementation of the protocol interface which defines how an input shape becomes and http::Request - * and an output shape is build from an `http::Response`. - */ - private val protocol: Protocol, -) { - protected val symbolProvider = codegenContext.symbolProvider - protected val model = codegenContext.model + fun generatePayload( + writer: RustWriter, + shapeName: String, + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext = object : AdditionalPayloadContext {}, + ) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt index 7812f917bb..0a53422552 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt @@ -143,15 +143,14 @@ open class AwsJson( override fun additionalRequestHeaders(operationShape: OperationShape): List> = listOf("x-amz-target" to "${codegenContext.serviceShape.id.name}.${operationShape.id.name}") - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return JsonParserGenerator( + override fun structuredDataParser(): StructuredDataParserGenerator = + JsonParserGenerator( codegenContext, httpBindingResolver, ::awsJsonFieldName, ) - } - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = AwsJsonSerializerGenerator(codegenContext, httpBindingResolver) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt index 9d12cfbb62..934b703eba 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt @@ -51,10 +51,10 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = + override fun structuredDataParser(): StructuredDataParserGenerator = AwsQueryParserGenerator(codegenContext, awsQueryErrors) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = AwsQuerySerializerGenerator(codegenContext) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt index 88fb69d015..489f1de582 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt @@ -64,11 +64,11 @@ class AwsQueryCompatible( override val defaultTimestampFormat = awsJson.defaultTimestampFormat - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = - awsJson.structuredDataParser(operationShape) + override fun structuredDataParser(): StructuredDataParserGenerator = + awsJson.structuredDataParser() - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = - awsJson.structuredDataSerializer(operationShape) + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = + awsJson.structuredDataSerializer() override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt index 01a530d46a..215865f4cb 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt @@ -42,11 +42,10 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return Ec2QueryParserGenerator(codegenContext, ec2QueryErrors) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + Ec2QueryParserGenerator(codegenContext, ec2QueryErrors) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = Ec2QuerySerializerGenerator(codegenContext) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt index bb55883a11..2c6ffc662c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt @@ -18,7 +18,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate -import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext @@ -26,6 +25,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.EventStreamErrorMarshallerGenerator @@ -42,10 +42,19 @@ import software.amazon.smithy.rust.codegen.core.util.isOutputEventStream import software.amazon.smithy.rust.codegen.core.util.isStreaming import software.amazon.smithy.rust.codegen.core.util.outputShape +data class EventStreamBodyParams( + val outerName: String, + val memberName: String, + val marshallerConstructorFn: RuntimeType, + val errorMarshallerConstructorFn: RuntimeType, + val additionalPayloadContext: AdditionalPayloadContext, +) + class HttpBoundProtocolPayloadGenerator( codegenContext: CodegenContext, private val protocol: Protocol, private val httpMessageType: HttpMessageType = HttpMessageType.REQUEST, + private val renderEventStreamBody: (RustWriter, EventStreamBodyParams) -> Unit, ) : ProtocolPayloadGenerator { private val symbolProvider = codegenContext.symbolProvider private val model = codegenContext.model @@ -62,7 +71,10 @@ class HttpBoundProtocolPayloadGenerator( ) private val protocolFunctions = ProtocolFunctions(codegenContext) - override fun payloadMetadata(operationShape: OperationShape): ProtocolPayloadGenerator.PayloadMetadata { + override fun payloadMetadata( + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ): ProtocolPayloadGenerator.PayloadMetadata { val (shape, payloadMemberName) = when (httpMessageType) { HttpMessageType.RESPONSE -> operationShape.outputShape(model) to httpBindingResolver.responseMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName @@ -91,50 +103,62 @@ class HttpBoundProtocolPayloadGenerator( } } - override fun generatePayload(writer: RustWriter, self: String, operationShape: OperationShape) { + override fun generatePayload( + writer: RustWriter, + shapeName: String, + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { when (httpMessageType) { - HttpMessageType.RESPONSE -> generateResponsePayload(writer, self, operationShape) - HttpMessageType.REQUEST -> generateRequestPayload(writer, self, operationShape) + HttpMessageType.RESPONSE -> generateResponsePayload(writer, shapeName, operationShape, additionalPayloadContext) + HttpMessageType.REQUEST -> generateRequestPayload(writer, shapeName, operationShape, additionalPayloadContext) } } - private fun generateRequestPayload(writer: RustWriter, self: String, operationShape: OperationShape) { + private fun generateRequestPayload( + writer: RustWriter, shapeName: String, operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { val payloadMemberName = httpBindingResolver.requestMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName if (payloadMemberName == null) { - val serializerGenerator = protocol.structuredDataSerializer(operationShape) - generateStructureSerializer(writer, self, serializerGenerator.operationInputSerializer(operationShape)) + val serializerGenerator = protocol.structuredDataSerializer() + generateStructureSerializer(writer, shapeName, serializerGenerator.operationInputSerializer(operationShape)) } else { - generatePayloadMemberSerializer(writer, self, operationShape, payloadMemberName) + generatePayloadMemberSerializer(writer, shapeName, operationShape, payloadMemberName, additionalPayloadContext) } } - private fun generateResponsePayload(writer: RustWriter, self: String, operationShape: OperationShape) { + private fun generateResponsePayload( + writer: RustWriter, shapeName: String, operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { val payloadMemberName = httpBindingResolver.responseMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName if (payloadMemberName == null) { - val serializerGenerator = protocol.structuredDataSerializer(operationShape) - generateStructureSerializer(writer, self, serializerGenerator.operationOutputSerializer(operationShape)) + val serializerGenerator = protocol.structuredDataSerializer() + generateStructureSerializer(writer, shapeName, serializerGenerator.operationOutputSerializer(operationShape)) } else { - generatePayloadMemberSerializer(writer, self, operationShape, payloadMemberName) + generatePayloadMemberSerializer(writer, shapeName, operationShape, payloadMemberName, additionalPayloadContext) } } private fun generatePayloadMemberSerializer( writer: RustWriter, - self: String, + shapeName: String, operationShape: OperationShape, payloadMemberName: String, + additionalPayloadContext: AdditionalPayloadContext, ) { - val serializerGenerator = protocol.structuredDataSerializer(operationShape) + val serializerGenerator = protocol.structuredDataSerializer() if (operationShape.isEventStream(model)) { if (operationShape.isInputEventStream(model) && target == CodegenTarget.CLIENT) { val payloadMember = operationShape.inputShape(model).expectMember(payloadMemberName) - writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, "self") + writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, shapeName, additionalPayloadContext) } else if (operationShape.isOutputEventStream(model) && target == CodegenTarget.SERVER) { val payloadMember = operationShape.outputShape(model).expectMember(payloadMemberName) - writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, "output") + writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, "output", additionalPayloadContext) } else { throw CodegenException("Payload serializer for event streams with an invalid configuration") } @@ -144,16 +168,16 @@ class HttpBoundProtocolPayloadGenerator( HttpMessageType.RESPONSE -> operationShape.outputShape(model).expectMember(payloadMemberName) HttpMessageType.REQUEST -> operationShape.inputShape(model).expectMember(payloadMemberName) } - writer.serializeViaPayload(bodyMetadata, self, payloadMember, serializerGenerator) + writer.serializeViaPayload(bodyMetadata, shapeName, payloadMember, serializerGenerator) } } - private fun generateStructureSerializer(writer: RustWriter, self: String, serializer: RuntimeType?) { + private fun generateStructureSerializer(writer: RustWriter, shapeName: String, serializer: RuntimeType?) { if (serializer == null) { writer.rust("\"\"") } else { writer.rust( - "#T(&$self)?", + "#T(&$shapeName)?", serializer, ) } @@ -164,6 +188,7 @@ class HttpBoundProtocolPayloadGenerator( memberShape: MemberShape, serializerGenerator: StructuredDataSerializerGenerator, outerName: String, + additionalPayloadContext: AdditionalPayloadContext, ) { val memberName = symbolProvider.toMemberName(memberShape) val unionShape = model.expectShape(memberShape.target, UnionShape::class.java) @@ -193,53 +218,31 @@ class HttpBoundProtocolPayloadGenerator( // TODO(EventStream): [RPC] RPC protocols need to send an initial message with the // parameters that are not `@eventHeader` or `@eventPayload`. - when (target) { - CodegenTarget.CLIENT -> - rustTemplate( - """ - { - let error_marshaller = #{errorMarshallerConstructorFn}(); - let marshaller = #{marshallerConstructorFn}(); - let signer = _config.new_event_stream_signer(properties.clone()); - let adapter: #{SmithyHttp}::event_stream::MessageStreamAdapter<_, _> = - $outerName.$memberName.into_body_stream(marshaller, error_marshaller, signer); - let body: #{SdkBody} = #{hyper}::Body::wrap_stream(adapter).into(); - body - } - """, - *codegenScope, - "marshallerConstructorFn" to marshallerConstructorFn, - "errorMarshallerConstructorFn" to errorMarshallerConstructorFn, - ) - CodegenTarget.SERVER -> { - rustTemplate( - """ - { - let error_marshaller = #{errorMarshallerConstructorFn}(); - let marshaller = #{marshallerConstructorFn}(); - let signer = #{NoOpSigner}{}; - let adapter: #{SmithyHttp}::event_stream::MessageStreamAdapter<_, _> = - $outerName.$memberName.into_body_stream(marshaller, error_marshaller, signer); - adapter - } - """, - *codegenScope, - "marshallerConstructorFn" to marshallerConstructorFn, - "errorMarshallerConstructorFn" to errorMarshallerConstructorFn, - ) - } - } + renderEventStreamBody( + this, + EventStreamBodyParams( + outerName, + memberName, + marshallerConstructorFn, + errorMarshallerConstructorFn, + additionalPayloadContext, + ), + ) } private fun RustWriter.serializeViaPayload( payloadMetadata: ProtocolPayloadGenerator.PayloadMetadata, - self: String, + shapeName: String, member: MemberShape, serializerGenerator: StructuredDataSerializerGenerator, ) { val ref = if (payloadMetadata.takesOwnership) "" else "&" val serializer = protocolFunctions.serializeFn(member, fnNameSuffix = "http_payload") { fnName -> - val outputT = if (member.isStreaming(model)) symbolProvider.toSymbol(member) else RuntimeType.ByteSlab.toSymbol() + val outputT = if (member.isStreaming(model)) { + symbolProvider.toSymbol(member) + } else { + RuntimeType.ByteSlab.toSymbol() + } rustBlockTemplate( "pub fn $fnName(payload: $ref#{Member}) -> Result<#{outputT}, #{BuildError}>", "Member" to symbolProvider.toSymbol(member), @@ -277,7 +280,7 @@ class HttpBoundProtocolPayloadGenerator( } } } - rust("#T($ref $self.${symbolProvider.toMemberName(member)})?", serializer) + rust("#T($ref $shapeName.${symbolProvider.toMemberName(member)})?", serializer) } private fun RustWriter.renderPayload( diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt index ec9b944e3e..4a1339ca9a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt @@ -38,10 +38,10 @@ interface Protocol { fun additionalErrorResponseHeaders(errorShape: StructureShape): List> = emptyList() /** Returns a deserialization code generator for this protocol */ - fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator + fun structuredDataParser(): StructuredDataParserGenerator /** Returns a serialization code generator for this protocol */ - fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator + fun structuredDataSerializer(): StructuredDataSerializerGenerator /** * Generates a function signature like the following: diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt index 675a0b2126..ba526d0c87 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt @@ -80,20 +80,24 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { * RestJson1 implementations can denote errors in responses in several ways. * New server-side protocol implementations MUST use a header field named `X-Amzn-Errortype`. * - * Note that the spec says that implementations SHOULD strip the error shape ID's namespace. - * However, our server implementation renders the full shape ID (including namespace), since some - * existing clients rely on it to deserialize the error shape and fail if only the shape name is present. - * This is compliant with the spec, see https://github.com/awslabs/smithy/pull/1493. - * See https://github.com/awslabs/smithy/issues/1494 too. + * Note that the spec says that implementations SHOULD strip the error shape ID's namespace + * (see https://smithy.io/2.0/aws/protocols/aws-restjson1-protocol.html#operation-error-serialization): + * + * > The value of this component SHOULD contain only the shape name of the error's Shape ID. + * + * But it's a SHOULD; we could strip the namespace if we wanted to. In fact, we did so in smithy-rs versions + * 0.52.0 to 0.55.4; see: + * - https://github.com/awslabs/smithy-rs/pull/1982 + * - https://github.com/awslabs/smithy/pull/1493 + * - https://github.com/awslabs/smithy/issues/1494 */ override fun additionalErrorResponseHeaders(errorShape: StructureShape): List> = - listOf("x-amzn-errortype" to errorShape.id.toString()) + listOf("x-amzn-errortype" to errorShape.id.name) - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = JsonSerializerGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt index 3045cf0268..41df9fd52d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt @@ -40,13 +40,11 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return RestXmlParserGenerator(codegenContext, restXmlErrors) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + RestXmlParserGenerator(codegenContext, restXmlErrors) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator { - return XmlBindingTraitSerializerGenerator(codegenContext, httpBindingResolver) - } + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = + XmlBindingTraitSerializerGenerator(codegenContext, httpBindingResolver) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 4fa98fa6ee..6b706a1e09 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -276,7 +276,7 @@ class EventStreamUnmarshallerGenerator( is StringShape -> { rustTemplate( """ - std::str::from_utf8(message.payload()) + ::std::str::from_utf8(message.payload()) .map_err(|_| #{Error}::unmarshalling("message payload is not valid UTF-8"))? .to_owned() """, @@ -294,7 +294,7 @@ class EventStreamUnmarshallerGenerator( private fun RustWriter.renderParseProtocolPayload(member: MemberShape) { val memberName = symbolProvider.toMemberName(member) - val parser = protocol.structuredDataParser(operationShape).payloadParser(member) + val parser = protocol.structuredDataParser().payloadParser(member) rustTemplate( """ #{parser}(&message.payload()[..]) @@ -341,7 +341,7 @@ class EventStreamUnmarshallerGenerator( when (codegenTarget) { CodegenTarget.CLIENT -> { val target = model.expectShape(member.target, StructureShape::class.java) - val parser = protocol.structuredDataParser(operationShape).errorParser(target) + val parser = protocol.structuredDataParser().errorParser(target) if (parser != null) { rust("let mut builder = #T::default();", symbolProvider.symbolForBuilder(target)) rustTemplate( @@ -363,7 +363,7 @@ class EventStreamUnmarshallerGenerator( CodegenTarget.SERVER -> { val target = model.expectShape(member.target, StructureShape::class.java) - val parser = protocol.structuredDataParser(operationShape).errorParser(target) + val parser = protocol.structuredDataParser().errorParser(target) val mut = if (parser != null) { " mut" } else { "" } rust("let$mut builder = #T::default();", symbolProvider.symbolForBuilder(target)) if (parser != null) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index dd7255e47e..01c2b64e6a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -39,7 +39,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section -import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName @@ -61,6 +60,12 @@ import software.amazon.smithy.utils.StringUtils */ sealed class JsonParserSection(name: String) : Section(name) { data class BeforeBoxingDeserializedMember(val shape: MemberShape) : JsonParserSection("BeforeBoxingDeserializedMember") + + data class AfterTimestampDeserializedMember(val shape: MemberShape) : JsonParserSection("AfterTimestampDeserializedMember") + + data class AfterBlobDeserializedMember(val shape: MemberShape) : JsonParserSection("AfterBlobDeserializedMember") + + data class AfterDocumentDeserializedMember(val shape: MemberShape) : JsonParserSection("AfterDocumentDeserializedMember") } /** @@ -94,7 +99,6 @@ class JsonParserGenerator( private val runtimeConfig = codegenContext.runtimeConfig private val codegenTarget = codegenContext.target private val smithyJson = CargoDependency.smithyJson(runtimeConfig).toType() - private val typeConversionGenerator = TypeConversionGenerator(model, symbolProvider, runtimeConfig) private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( "Error" to smithyJson.resolve("deserialize::error::DeserializeError"), @@ -150,14 +154,15 @@ class JsonParserGenerator( override fun payloadParser(member: MemberShape): RuntimeType { val shape = model.expectShape(member.target) + val returnSymbolToParse = returnSymbolToParse(shape) check(shape is UnionShape || shape is StructureShape || shape is DocumentShape) { - "payload parser should only be used on structures & unions" + "Payload parser should only be used on structure shapes, union shapes, and document shapes." } return protocolFunctions.deserializeFn(shape, fnNameSuffix = "payload") { fnName -> rustBlockTemplate( - "pub fn $fnName(input: &[u8]) -> Result<#{Shape}, #{Error}>", + "pub(crate) fn $fnName(input: &[u8]) -> Result<#{ReturnType}, #{Error}>", *codegenScope, - "Shape" to symbolProvider.toSymbol(shape), + "ReturnType" to returnSymbolToParse.symbol, ) { val input = if (shape is DocumentShape) { "input" @@ -276,13 +281,13 @@ class JsonParserGenerator( is StringShape -> deserializeString(target) is BooleanShape -> rustTemplate("#{expect_bool_or_null}(tokens.next())?", *codegenScope) is NumberShape -> deserializeNumber(target) - is BlobShape -> deserializeBlob() + is BlobShape -> deserializeBlob(memberShape) is TimestampShape -> deserializeTimestamp(target, memberShape) is CollectionShape -> deserializeCollection(target) is MapShape -> deserializeMap(target) is StructureShape -> deserializeStruct(target) is UnionShape -> deserializeUnion(target) - is DocumentShape -> rustTemplate("Some(#{expect_document}(tokens)?)", *codegenScope) + is DocumentShape -> deserializeDocument(memberShape) else -> PANIC("unexpected shape: $target") } val symbol = symbolProvider.toSymbol(memberShape) @@ -294,11 +299,21 @@ class JsonParserGenerator( } } - private fun RustWriter.deserializeBlob() { + private fun RustWriter.deserializeDocument(member: MemberShape) { + rustTemplate("Some(#{expect_document}(tokens)?)", *codegenScope) + for (customization in customizations) { + customization.section(JsonParserSection.AfterDocumentDeserializedMember(member))(this) + } + } + + private fun RustWriter.deserializeBlob(member: MemberShape) { rustTemplate( "#{expect_blob_or_null}(tokens.next())?", *codegenScope, ) + for (customization in customizations) { + customization.section(JsonParserSection.AfterBlobDeserializedMember(member))(this) + } } private fun RustWriter.deserializeStringInner(target: StringShape, escapedStrName: String) { @@ -349,9 +364,12 @@ class JsonParserGenerator( ) val timestampFormatType = RuntimeType.parseTimestampFormat(codegenTarget, runtimeConfig, timestampFormat) rustTemplate( - "#{expect_timestamp_or_null}(tokens.next(), #{T})?#{ConvertFrom:W}", - "T" to timestampFormatType, "ConvertFrom" to typeConversionGenerator.convertViaFrom(shape), *codegenScope, + "#{expect_timestamp_or_null}(tokens.next(), #{T})?", + "T" to timestampFormatType, *codegenScope, ) + for (customization in customizations) { + customization.section(JsonParserSection.AfterTimestampDeserializedMember(member))(this) + } } private fun RustWriter.deserializeCollection(shape: CollectionShape) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index 33b06973b0..87562dc2ab 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -26,12 +26,14 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.TimestampShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.TimestampFormatTrait.Format.EPOCH_SECONDS +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider @@ -312,21 +314,25 @@ class JsonSerializerGenerator( context: StructContext, includedMembers: List? = null, ) { - val structureSymbol = symbolProvider.toSymbol(context.shape) val structureSerializer = protocolFunctions.serializeFn(context.shape) { fnName -> + val inner = context.copy(objectName = "object", localName = "input") + val members = includedMembers ?: inner.shape.members() + val allowUnusedVariables = writable { + if (members.isEmpty()) { Attribute.AllowUnusedVariables.render(this) } + } rustBlockTemplate( - "pub fn $fnName(object: &mut #{JsonObjectWriter}, input: &#{Input}) -> Result<(), #{Error}>", - "Input" to structureSymbol, + """ + pub fn $fnName( + #{AllowUnusedVariables:W} object: &mut #{JsonObjectWriter}, + #{AllowUnusedVariables:W} input: &#{StructureSymbol}, + ) -> Result<(), #{Error}> + """, + "StructureSymbol" to symbolProvider.toSymbol(context.shape), + "AllowUnusedVariables" to allowUnusedVariables, *codegenScope, ) { - context.copy(objectName = "object", localName = "input").also { inner -> - val members = includedMembers ?: inner.shape.members() - if (members.isEmpty()) { - rust("let (_, _) = (object, input);") // Suppress unused argument warnings - } - for (member in members) { - serializeMember(MemberContext.structMember(inner, member, symbolProvider, jsonName)) - } + for (member in members) { + serializeMember(MemberContext.structMember(inner, member, symbolProvider, jsonName)) } rust("Ok(())") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt index d1f208c393..4c1d435204 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt @@ -24,6 +24,7 @@ data class IntegrationTestParams( val additionalSettings: ObjectNode = ObjectNode.builder().build(), val overrideTestDir: File? = null, val command: ((Path) -> Unit)? = null, + val cargoCommand: String? = null, ) /** @@ -40,6 +41,6 @@ fun codegenIntegrationTest(model: Model, params: IntegrationTestParams, invokePl ) invokePlugin(ctx) ctx.fileManifest.printGeneratedFiles() - params.command?.invoke(testDir) ?: "cargo test".runCommand(testDir, environment = mapOf("RUSTFLAGS" to "-D warnings")) + params.command?.invoke(testDir) ?: (params.cargoCommand ?: "cargo test").runCommand(testDir, environment = mapOf("RUSTFLAGS" to "-D warnings")) return testDir } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt new file mode 100644 index 0000000000..c45a7d0992 --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt @@ -0,0 +1,172 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.testutil + +import software.amazon.smithy.model.Model +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope + +object NamingObstacleCourseTestModels { + private val rustPrelude = preludeScope.map { pair -> pair.first } + + /** + * Test model that confounds the generation machinery by using operations named after every item + * in the Rust prelude. + */ + fun rustPreludeOperationsModel(): Model = StringBuilder().apply { + append( + """ + ${"$"}version: "2.0" + namespace crate + + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + use aws.protocols#awsJson1_1 + use aws.api#service + use smithy.framework#ValidationException + + structure InputAndOutput {} + + @awsJson1_1 + @service(sdkId: "Config") + service Config { + version: "2006-03-01", + rename: { "smithy.api#String": "PreludeString" }, + operations: [ + """, + ) + for (item in rustPrelude) { + append("$item,\n") + } + append( + """ + ] + } + """, + ) + for (item in rustPrelude) { + append("operation $item { input: InputAndOutput, output: InputAndOutput, errors: [ValidationException] }\n") + } + }.toString().asSmithyModel() + + fun rustPreludeStructsModel(): Model = StringBuilder().apply { + append( + """ + ${"$"}version: "2.0" + namespace crate + + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + use aws.protocols#awsJson1_1 + use aws.api#service + use smithy.framework#ValidationException + + structure InputAndOutput {} + + @awsJson1_1 + @service(sdkId: "Config") + service Config { + version: "2006-03-01", + rename: { "smithy.api#String": "PreludeString" }, + operations: [ + """, + ) + for (item in rustPrelude) { + append("Use$item,\n") + } + append( + """ + ] + } + """, + ) + for (item in rustPrelude) { + append("structure $item { $item: smithy.api#String }\n") + append("operation Use$item { input: $item, output: $item, errors: [ValidationException] }\n") + } + println(toString()) + }.toString().asSmithyModel() + + fun rustPreludeEnumsModel(): Model = StringBuilder().apply { + append( + """ + ${"$"}version: "2.0" + namespace crate + + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + use aws.protocols#awsJson1_1 + use aws.api#service + use smithy.framework#ValidationException + + structure InputAndOutput {} + + @awsJson1_1 + @service(sdkId: "Config") + service Config { + version: "2006-03-01", + rename: { "smithy.api#String": "PreludeString" }, + operations: [ + """, + ) + for (item in rustPrelude) { + append("Use$item,\n") + } + append( + """ + ] + } + """, + ) + for (item in rustPrelude) { + append("enum $item { $item }\n") + append("structure Struct$item { $item: $item }\n") + append("operation Use$item { input: Struct$item, output: Struct$item, errors: [ValidationException] }\n") + } + }.toString().asSmithyModel() + + fun rustPreludeEnumVariantsModel(): Model = StringBuilder().apply { + append( + """ + ${"$"}version: "2.0" + namespace crate + + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + use aws.protocols#awsJson1_1 + use aws.api#service + use smithy.framework#ValidationException + + @awsJson1_1 + @service(sdkId: "Config") + service Config { + version: "2006-03-01", + rename: { "smithy.api#String": "PreludeString" }, + operations: [EnumOp] + } + + operation EnumOp { + input: InputAndOutput, + output: InputAndOutput, + errors: [ValidationException], + } + + structure InputAndOutput { + the_enum: TheEnum, + } + + enum TheEnum { + """, + ) + for (item in rustPrelude) { + append("$item,\n") + } + append( + """ + } + """, + ) + }.toString().asSmithyModel() +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt index 5f9e612ee7..0a4a52f839 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt @@ -32,8 +32,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.util.CommandFailed -import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.smithy.generators.CargoTomlGenerator +import software.amazon.smithy.rust.codegen.core.smithy.mergeDependencyFeatures +import software.amazon.smithy.rust.codegen.core.smithy.mergeIdenticalTestDependencies +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.orNullIfEmpty import software.amazon.smithy.rust.codegen.core.util.runCommand @@ -109,7 +111,7 @@ object TestWorkspace { // help rust select the right version when we run cargo test // TODO(https://github.com/awslabs/smithy-rs/issues/2048): load this from the msrv property using a // method as we do for runtime crate versions - "[toolchain]\nchannel = \"1.66.1\"\n", + "[toolchain]\nchannel = \"1.69.0\"\n", ) // ensure there at least an empty lib.rs file to avoid broken crates newProject.resolve("src").mkdirs() @@ -215,10 +217,12 @@ fun RustWriter.unitTest( name: String, vararg args: Any, attribute: Attribute = Attribute.Test, + additionalAttributes: List = emptyList(), async: Boolean = false, block: Writable, ): RustWriter { attribute.render(this) + additionalAttributes.forEach { it.render(this) } if (async) { rust("async") } @@ -367,14 +371,19 @@ fun RustWriter.compileAndTest( clippy: Boolean = false, expectFailure: Boolean = false, ): String { - val deps = this.dependencies.map { RustDependency.fromSymbolDependency(it) }.filterIsInstance() + val deps = this.dependencies + .map { RustDependency.fromSymbolDependency(it) } + .filterIsInstance() + .distinct() + .mergeDependencyFeatures() + .mergeIdenticalTestDependencies() val module = if (this.namespace.contains("::")) { this.namespace.split("::")[1] } else { "lib" } val tempDir = this.toString() - .intoCrate(deps.toSet(), module = module, main = main, strict = clippy) + .intoCrate(deps, module = module, main = main, strict = clippy) val mainRs = tempDir.resolve("src/main.rs") val testModule = tempDir.resolve("src/$module.rs") try { @@ -387,7 +396,7 @@ fun RustWriter.compileAndTest( println("Test sources for debugging: file://${testModule.absolutePath}") } return testOutput - } catch (e: CommandFailed) { + } catch (e: CommandError) { if (!expectFailure) { println("Test sources for debugging: file://${testModule.absolutePath}") } @@ -396,23 +405,25 @@ fun RustWriter.compileAndTest( } private fun String.intoCrate( - deps: Set, + deps: List, module: String? = null, main: String = "", strict: Boolean = false, ): File { this.shouldParseAsRust() val tempDir = TestWorkspace.subproject() - val cargoToml = """ - [package] - name = ${tempDir.nameWithoutExtension.dq()} - version = "0.0.1" - authors = ["rcoh@amazon.com"] - edition = "2021" - - [dependencies] - ${deps.joinToString("\n") { it.toString() }} - """.trimIndent() + val cargoToml = RustWriter.toml("Cargo.toml").apply { + CargoTomlGenerator( + moduleName = tempDir.nameWithoutExtension, + moduleVersion = "0.0.1", + moduleAuthors = listOf("Testy McTesterson"), + moduleDescription = null, + moduleLicense = null, + moduleRepository = null, + writer = this, + dependencies = deps, + ).render() + }.toString() tempDir.resolve("Cargo.toml").writeText(cargoToml) tempDir.resolve("src").mkdirs() val mainRs = tempDir.resolve("src/main.rs") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Exec.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Exec.kt index 7609e950a2..f7a08f62f3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Exec.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Exec.kt @@ -9,7 +9,7 @@ import java.io.IOException import java.nio.file.Path import java.util.concurrent.TimeUnit -data class CommandFailed(val output: String) : Exception("Command Failed\n$output") +data class CommandError(val output: String) : Exception("Command Error\n$output") fun String.runCommand(workdir: Path? = null, environment: Map = mapOf(), timeout: Long = 3600): String { val parts = this.split("\\s".toRegex()) @@ -30,13 +30,13 @@ fun String.runCommand(workdir: Path? = null, environment: Map = val output = "$stdErr\n$stdOut" return when (proc.exitValue()) { 0 -> output - else -> throw CommandFailed("Command Failed\n$output") + else -> throw CommandError("Command Error\n$output") } } catch (_: IllegalThreadStateException) { - throw CommandFailed("Timeout") + throw CommandError("Timeout") } catch (err: IOException) { - throw CommandFailed("$this was not a valid command.\n Hint: is everything installed?\n$err") + throw CommandError("$this was not a valid command.\n Hint: is everything installed?\n$err") } catch (other: Exception) { - throw CommandFailed("Unexpected exception thrown when executing subprocess:\n$other") + throw CommandError("Unexpected exception thrown when executing subprocess:\n$other") } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt index 299cdff24d..bde5c4b338 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.core.util +import software.amazon.smithy.aws.traits.ServiceTrait import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BooleanShape @@ -146,3 +147,7 @@ fun String.shapeId() = ShapeId.from(this) /** Returns the service name, or a default value if the service doesn't have a title trait */ fun ServiceShape.serviceNameOrDefault(default: String) = getTrait()?.value ?: default + +/** Returns the SDK ID of the given service shape */ +fun ServiceShape.sdkId(): String = + getTrait()?.sdkId?.lowercase()?.replace(" ", "") ?: id.getName(this) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependencyTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependencyTest.kt new file mode 100644 index 0000000000..9d18c4725e --- /dev/null +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependencyTest.kt @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.rustlang + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class CargoDependencyTest { + @Test + fun `it should not allow a dependency with test-util in non-dev scopes`() { + // OK + CargoDependency( + name = "test", + location = CratesIo("1.0"), + features = setOf("foo", "test-util", "bar"), + scope = DependencyScope.Dev, + ) + + // OK + CargoDependency( + name = "test", + location = CratesIo("1.0"), + features = setOf("foo", "bar"), + scope = DependencyScope.Dev, + ).toDevDependency().withFeature("test-util") + + assertThrows { + CargoDependency( + name = "test", + location = CratesIo("1.0"), + features = setOf("foo", "test-util", "bar"), + scope = DependencyScope.Compile, + ) + } + + assertThrows { + CargoDependency( + name = "test", + location = CratesIo("1.0"), + features = setOf("foo", "bar"), + scope = DependencyScope.Compile, + ).withFeature("test-util") + } + } +} diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/InlineDependencyTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/InlineDependencyTest.kt index 9d613f558f..4e341c769f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/InlineDependencyTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/InlineDependencyTest.kt @@ -10,6 +10,7 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest @@ -32,20 +33,23 @@ internal class InlineDependencyTest { @Test fun `locate dependencies from the inlineable module`() { - val dep = InlineDependency.idempotencyToken() + val runtimeConfig = TestRuntimeConfig + val dep = InlineDependency.serializationSettings(runtimeConfig) val testProject = TestWorkspace.testProject() testProject.lib { rustTemplate( """ + ##[test] - fn idempotency_works() { - use #{idempotency}::uuid_v4; - let res = uuid_v4(0); - assert_eq!(res, "00000000-0000-4000-8000-000000000000"); + fn header_serialization_settings_can_be_constructed() { + use #{serialization_settings}::HeaderSerializationSettings; + use #{aws_smithy_http}::header::set_request_header_if_absent; + let _settings = HeaderSerializationSettings::default(); } """, - "idempotency" to dep.toType(), + "serialization_settings" to dep.toType(), + "aws_smithy_http" to RuntimeType.smithyHttp(runtimeConfig), ) } testProject.compileAndTest() diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustTypeTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustTypeTest.kt index 7fa364dfaf..1c89f39058 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustTypeTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustTypeTest.kt @@ -54,14 +54,14 @@ internal class RustTypesTest { @Test fun `RustType_String_writable produces a template-compatible RuntimeType`() { - forInputExpectOutput(RustType.String.writable, "'std::string::String'") + forInputExpectOutput(RustType.String.writable, "'::std::string::String'") } @Test fun `RustType_Vec_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Vec(RustType.String).writable, - "'std::vec::Vec'", + "'::std::vec::Vec<::std::string::String>'", ) } @@ -69,7 +69,7 @@ internal class RustTypesTest { fun `RustType_Slice_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Slice(RustType.String).writable, - "'[std::string::String]'", + "'[::std::string::String]'", ) } @@ -77,7 +77,7 @@ internal class RustTypesTest { fun `RustType_HashMap_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.HashMap(RustType.String, RustType.String).writable, - "'std::collections::HashMap'", + "'::std::collections::HashMap<::std::string::String, ::std::string::String>'", ) } @@ -87,7 +87,7 @@ internal class RustTypesTest { RustType.HashSet(RustType.String).writable, // Rust doesn't guarantee that `HashSet`s are insertion ordered, so we use a `Vec` instead. // This is called out in a comment in the RustType.HashSet declaration - "'std::vec::Vec'", + "'::std::vec::Vec<::std::string::String>'", ) } @@ -95,15 +95,15 @@ internal class RustTypesTest { fun `RustType_Reference_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Reference("&", RustType.String).writable, - "'&std::string::String'", + "'&::std::string::String'", ) forInputExpectOutput( RustType.Reference("&mut", RustType.String).writable, - "'&mut std::string::String'", + "'&mut ::std::string::String'", ) forInputExpectOutput( RustType.Reference("&'static", RustType.String).writable, - "&'static std::string::String'", + "&'static ::std::string::String'", ) } @@ -111,7 +111,7 @@ internal class RustTypesTest { fun `RustType_Option_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Option(RustType.String).writable, - "'std::option::Option'", + "'::std::option::Option<::std::string::String>'", ) } @@ -119,7 +119,7 @@ internal class RustTypesTest { fun `RustType_Box_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Box(RustType.String).writable, - "'std::boxed::Box'", + "'::std::boxed::Box<::std::string::String>'", ) } @@ -147,7 +147,7 @@ internal class RustTypesTest { fun `types render properly`() { val type = RustType.Box(RustType.Option(RustType.Reference("a", RustType.Vec(RustType.String)))) type.render(false) shouldBe "Box>>" - type.render(true) shouldBe "std::boxed::Box>>" + type.render(true) shouldBe "::std::boxed::Box<::std::option::Option<&'a ::std::vec::Vec<::std::string::String>>>" } @Test @@ -211,7 +211,7 @@ internal class RustTypesTest { writable { attributeMacro.render(this) }, - "#[derive(std::clone::Clone, std::error::Error, std::fmt::Debug)]\n", + "#[derive(::std::clone::Clone, ::std::error::Error, ::std::fmt::Debug)]\n", ) } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt index 3b0ef7c557..2bd5269cc2 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt @@ -133,6 +133,19 @@ class RustWriterTest { sut.toString().shouldContain("#[foo]\n/// here's an attribute") } + @Test + fun `attributes with derive helpers must come after derives`() { + val attr = Attribute("foo", isDeriveHelper = true) + val metadata = RustMetadata( + derives = setOf(RuntimeType.Debug), + additionalAttributes = listOf(Attribute.AllowDeprecated, attr), + visibility = Visibility.PUBLIC, + ) + val sut = RustWriter.root() + metadata.render(sut) + sut.toString().shouldContain("#[allow(deprecated)]\n#[derive(::std::fmt::Debug)]\n#[foo]") + } + @Test fun `deprecated attribute without any field`() { val sut = RustWriter.root() @@ -170,7 +183,7 @@ class RustWriterTest { "Inner" to inner, "http" to RuntimeType.Http.resolve("foo"), ) - sut.toString().shouldContain("inner: hello, regular: http::foo") + sut.toString().shouldContain("inner: hello, regular: ::http::foo") } @Test diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/WritableTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/WritableTest.kt index 7da0562451..04c5ff2f1a 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/WritableTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/WritableTest.kt @@ -33,7 +33,7 @@ internal class RustTypeParametersTest { @Test fun `rustTypeParameters accepts RuntimeType`() { val runtimeType = RuntimeType.String - forInputExpectOutput(runtimeType, "''") + forInputExpectOutput(runtimeType, "'<::std::string::String>'") } @Test @@ -60,7 +60,7 @@ internal class RustTypeParametersTest { writer.rustInlineTemplate("#{tps:W}", "tps" to tps) writer.rustInlineTemplate("'") - writer.toString() shouldContain "''" + writer.toString() shouldContain "''" } @Test diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegatorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegatorTest.kt index c9407372d7..6ed2fc5ed3 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegatorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegatorTest.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.smithy import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.RepeatedTest import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.CratesIo @@ -13,7 +14,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope.Compile import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope.Dev class CodegenDelegatorTest { - @Test + @RepeatedTest(10) // Test it several times since the shuffle adds in some randomness fun testMergeDependencyFeatures() { val merged = listOf( @@ -36,6 +37,22 @@ class CodegenDelegatorTest { ) } + @RepeatedTest(10) // Test it several times since the shuffle adds in some randomness + fun testMergeDependencyFeaturesDontMergeDevOnlyFeatures() { + val merged = listOf( + CargoDependency("A", CratesIo("1"), Compile, features = setOf("a")), + CargoDependency("A", CratesIo("1"), Compile, features = setOf("b")), + CargoDependency("A", CratesIo("1"), Dev, features = setOf("c")), + CargoDependency("A", CratesIo("1"), Dev, features = setOf("test-util")), + ).shuffled().mergeDependencyFeatures() + .sortedBy { it.scope } + + merged shouldBe setOf( + CargoDependency("A", CratesIo("1"), Compile, features = setOf("a", "b")), + CargoDependency("A", CratesIo("1"), Dev, features = setOf("c", "test-util")), + ) + } + @Test fun testMergeIdenticalFeatures() { val merged = listOf( @@ -43,11 +60,15 @@ class CodegenDelegatorTest { CargoDependency("A", CratesIo("1"), Dev), CargoDependency("B", CratesIo("1"), Compile), CargoDependency("B", CratesIo("1"), Dev, features = setOf("a", "b")), + CargoDependency("C", CratesIo("1"), Compile), + CargoDependency("C", CratesIo("1"), Dev, features = setOf("test-util")), ).mergeIdenticalTestDependencies() merged shouldBe setOf( CargoDependency("A", CratesIo("1"), Compile), CargoDependency("B", CratesIo("1"), Compile), CargoDependency("B", CratesIo("1"), Dev, features = setOf("a", "b")), + CargoDependency("C", CratesIo("1"), Compile), + CargoDependency("C", CratesIo("1"), Dev, features = setOf("test-util")), ) } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt index 120fc5cb20..55b792218f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt @@ -74,38 +74,38 @@ class SmithyTypesPubUseExtraTest { @Test fun `it re-exports Blob when a model uses blobs`() { - assertDoesntHaveType(typesWithEmptyModel(), "aws_smithy_types::Blob") - assertHasType(typesWithMember(inputMember = "foo: Blob"), "aws_smithy_types::Blob") - assertHasType(typesWithMember(outputMember = "foo: Blob"), "aws_smithy_types::Blob") + assertDoesntHaveType(typesWithEmptyModel(), "::aws_smithy_types::Blob") + assertHasType(typesWithMember(inputMember = "foo: Blob"), "::aws_smithy_types::Blob") + assertHasType(typesWithMember(outputMember = "foo: Blob"), "::aws_smithy_types::Blob") assertHasType( typesWithMember(inputMember = "foo: SomeUnion", unionMember = "foo: Blob"), - "aws_smithy_types::Blob", + "::aws_smithy_types::Blob", ) assertHasType( typesWithMember(outputMember = "foo: SomeUnion", unionMember = "foo: Blob"), - "aws_smithy_types::Blob", + "::aws_smithy_types::Blob", ) } @Test fun `it re-exports DateTime when a model uses timestamps`() { assertDoesntHaveType(typesWithEmptyModel(), "aws_smithy_types::DateTime") - assertHasType(typesWithMember(inputMember = "foo: Timestamp"), "aws_smithy_types::DateTime") - assertHasType(typesWithMember(outputMember = "foo: Timestamp"), "aws_smithy_types::DateTime") + assertHasType(typesWithMember(inputMember = "foo: Timestamp"), "::aws_smithy_types::DateTime") + assertHasType(typesWithMember(outputMember = "foo: Timestamp"), "::aws_smithy_types::DateTime") assertHasType( typesWithMember(inputMember = "foo: SomeUnion", unionMember = "foo: Timestamp"), - "aws_smithy_types::DateTime", + "::aws_smithy_types::DateTime", ) assertHasType( typesWithMember(outputMember = "foo: SomeUnion", unionMember = "foo: Timestamp"), - "aws_smithy_types::DateTime", + "::aws_smithy_types::DateTime", ) } @Test fun `it re-exports ByteStream and AggregatedBytes when a model has streaming`() { val streamingTypes = - listOf("aws_smithy_http::byte_stream::ByteStream", "aws_smithy_http::byte_stream::AggregatedBytes") + listOf("::aws_smithy_http::byte_stream::ByteStream", "::aws_smithy_http::byte_stream::AggregatedBytes") val streamingShape = "@streaming blob Streaming" assertDoesntHaveTypes(typesWithEmptyModel(), streamingTypes) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt index ec107f3fcb..fcec11601e 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt @@ -40,7 +40,10 @@ internal class BuilderGeneratorTest { unitTest("generate_builders") { rust( """ - let my_struct = MyStruct::builder().byte_value(4).foo("hello!").build(); + let my_struct_builder = MyStruct::builder().byte_value(4).foo("hello!"); + assert_eq!(*my_struct_builder.get_byte_value(), Some(4)); + + let my_struct = my_struct_builder.build(); assert_eq!(my_struct.foo.unwrap(), "hello!"); assert_eq!(my_struct.bar, 0); """, diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt index f765587300..77932ddfac 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import io.kotest.matchers.string.shouldContainInOrder import io.kotest.matchers.string.shouldNotContain -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -401,7 +400,7 @@ class StructureGeneratorTest { } @Test - fun `non-streaming fields are doc-hidden`() { + fun `fields are NOT doc-hidden`() { val model = """ namespace com.test structure MyStruct { @@ -415,9 +414,9 @@ class StructureGeneratorTest { val struct = model.lookup("com.test#MyStruct") val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) - RustWriter.forModule("test").let { - StructureGenerator(model, provider, it, struct, emptyList()).render() - assertEquals(6, it.toString().split("#[doc(hidden)]").size, "there should be 5 doc-hiddens") + RustWriter.forModule("test").let { writer -> + StructureGenerator(model, provider, writer, struct, emptyList()).render() + writer.toString().shouldNotContain("#[doc(hidden)]") } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt index 1dfc41795d..d204a605c4 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt @@ -17,7 +17,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.lookup class RecursiveShapesIntegrationTest { @@ -61,7 +61,7 @@ class RecursiveShapesIntegrationTest { project } val unmodifiedProject = check(model) - val output = assertThrows { + val output = assertThrows { unmodifiedProject.compileAndTest(expectFailure = true) } // THIS IS A LOAD-BEARING shouldContain! If the compiler error changes then this will break! diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/ExecKtTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/ExecKtTest.kt index aab2b70613..5041166fb7 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/ExecKtTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/ExecKtTest.kt @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test internal class ExecKtTest { @Test fun `missing command throws CommandFailed`() { - shouldThrow { + shouldThrow { "notaprogram run".runCommand() } } diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 2dbeebca0a..c4b2f00dba 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -84,12 +84,12 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> CodegenTest("com.amazonaws.ebs#Ebs", "ebs", imports = listOf("$commonModels/ebs.json")), CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), CodegenTest( - "com.aws.example.rust#PokemonService", + "com.aws.example#PokemonService", "pokemon-service-server-sdk", imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy"), ), CodegenTest( - "com.aws.example.rust#PokemonService", + "com.aws.example#PokemonService", "pokemon-service-awsjson-server-sdk", imports = listOf("$commonModels/pokemon-awsjson.smithy", "$commonModels/pokemon-common.smithy"), ), diff --git a/codegen-server-test/python/build.gradle.kts b/codegen-server-test/python/build.gradle.kts index 35144f42f0..469e0e6bd7 100644 --- a/codegen-server-test/python/build.gradle.kts +++ b/codegen-server-test/python/build.gradle.kts @@ -41,27 +41,63 @@ dependencies { val allCodegenTests = "../../codegen-core/common-test-models".let { commonModels -> listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple", imports = listOf("$commonModels/simple.smithy")), - CodegenTest("com.aws.example.python#PokemonService", "pokemon-service-server-sdk"), CodegenTest( - "com.amazonaws.ebs#Ebs", "ebs", + "com.aws.example#PokemonService", + "pokemon-service-server-sdk", + imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy"), + ), + CodegenTest( + "com.amazonaws.ebs#Ebs", + "ebs", imports = listOf("$commonModels/ebs.json"), - extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), CodegenTest( "aws.protocoltests.misc#MiscService", "misc", imports = listOf("$commonModels/misc.smithy"), - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) `@uniqueItems` is used. - extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), - // TODO(https://github.com/awslabs/smithy-rs/issues/2476) + CodegenTest( + "aws.protocoltests.json#JsonProtocol", + "json_rpc11", + ), + CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), + CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), + CodegenTest( + "aws.protocoltests.restjson#RestJsonExtras", + "rest_json_extras", + imports = listOf("$commonModels/rest-json-extras.smithy"), + ), + // TODO(https://github.com/awslabs/smithy-rs/issues/2477) // CodegenTest( - // "aws.protocoltests.json#JsonProtocol", - // "json_rpc11", + // "aws.protocoltests.restjson.validation#RestJsonValidation", + // "rest_json_validation", + // // `@range` trait is used on floating point shapes, which we deliberately don't want to support. + // // See https://github.com/awslabs/smithy-rs/issues/1401. // extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, // ), - // TODO(https://github.com/awslabs/smithy-rs/issues/2479) - // CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), + CodegenTest( + "com.amazonaws.constraints#ConstraintsService", + "constraints", + imports = listOf("$commonModels/constraints.smithy"), + ), + CodegenTest( + "com.amazonaws.constraints#ConstraintsService", + "constraints_without_public_constrained_types", + imports = listOf("$commonModels/constraints.smithy"), + extraConfig = """, "codegen": { "publicConstrainedTypes": false } """, + ), + CodegenTest( + "com.amazonaws.constraints#UniqueItemsService", + "unique_items", + imports = listOf("$commonModels/unique-items.smithy"), + ), + CodegenTest( + "naming_obs_structs#NamingObstacleCourseStructs", + "naming_test_structs", + imports = listOf("$commonModels/naming-obstacle-course-structs.smithy"), + ), + CodegenTest("casing#ACRONYMInside_Service", "naming_test_casing", imports = listOf("$commonModels/naming-obstacle-course-casing.smithy")), + CodegenTest("crate#Config", "naming_test_ops", imports = listOf("$commonModels/naming-obstacle-course-ops.smithy")), ) } @@ -69,6 +105,21 @@ project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) +tasks.register("stubs") { + description = "Generate Python stubs for all models" + dependsOn("assemble") + + doLast { + allCodegenTests.forEach { test -> + val crateDir = "$buildDir/$workingDirUnderBuildDir/${test.module}/$pluginName" + val moduleName = test.module.replace("-", "_") + exec { + commandLine("bash", "$crateDir/stubgen.sh", moduleName, "$crateDir/Cargo.toml", "$crateDir/python/$moduleName") + } + } + } +} + tasks["smithyBuildJar"].dependsOn("generateSmithyBuild") tasks["assemble"].finalizedBy("generateCargoWorkspace") diff --git a/codegen-server-test/python/model/pokemon.smithy b/codegen-server-test/python/model/pokemon.smithy deleted file mode 100644 index 4ad42a0b69..0000000000 --- a/codegen-server-test/python/model/pokemon.smithy +++ /dev/null @@ -1,45 +0,0 @@ -/// TODO(https://github.com/awslabs/smithy-rs/issues/1508) -/// $econcile this model with the main one living inside codegen-server-test/model/pokemon.smithy -/// once the Python implementation supports Streaming and Union shapes. -$version: "1.0" - -namespace com.aws.example.python - -use aws.protocols#restJson1 -use com.aws.example#PokemonSpecies -use com.aws.example#Storage -use com.aws.example#GetServerStatistics -use com.aws.example#DoNothing -use com.aws.example#CheckHealth -use smithy.framework#ValidationException - - -/// The Pokémon Service allows you to retrieve information about Pokémon species. -@title("Pokémon Service") -@restJson1 -service PokemonService { - version: "2021-12-01" - resources: [PokemonSpecies] - operations: [ - GetServerStatistics - DoNothing - CheckHealth - StreamPokemonRadio - ], -} - -/// Fetch the radio song from the database and stream it back as a playable audio. -@readonly -@http(uri: "/radio", method: "GET") -operation StreamPokemonRadio { - output: StreamPokemonRadioOutput -} - -@output -structure StreamPokemonRadioOutput { - @httpPayload - data: StreamingBlob -} - -@streaming -blob StreamingBlob diff --git a/codegen-server-test/typescript/build.gradle.kts b/codegen-server-test/typescript/build.gradle.kts new file mode 100644 index 0000000000..8b65d53239 --- /dev/null +++ b/codegen-server-test/typescript/build.gradle.kts @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +description = "Generates Rust/Typescript code from Smithy models and runs the protocol tests" +extra["displayName"] = "Smithy :: Rust :: Codegen :: Server :: Typescript :: Test" +extra["moduleName"] = "software.amazon.smithy.rust.kotlin.codegen.server.typescript.test" + +tasks["jar"].enabled = false + +plugins { + id("software.amazon.smithy") +} + +val smithyVersion: String by project +val defaultRustDocFlags: String by project +val properties = PropertyRetriever(rootProject, project) + +val pluginName = "rust-server-codegen-typescript" +val workingDirUnderBuildDir = "smithyprojections/codegen-server-test-typescript/" + +configure { + outputDirectory = file("$buildDir/$workingDirUnderBuildDir") +} + +buildscript { + val smithyVersion: String by project + dependencies { + classpath("software.amazon.smithy:smithy-cli:$smithyVersion") + } +} + +dependencies { + implementation(project(":codegen-server:typescript")) + implementation("software.amazon.smithy:smithy-aws-protocol-tests:$smithyVersion") + implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") + implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") +} + +val allCodegenTests = "../../codegen-core/common-test-models".let { commonModels -> + listOf( + CodegenTest("com.amazonaws.simple#SimpleService", "simple", imports = listOf("$commonModels/simple.smithy")), + CodegenTest("com.aws.example.ts#PokemonService", "pokemon-service-server-sdk"), + ) +} + +project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests) +project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) +project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) + +tasks["smithyBuildJar"].dependsOn("generateSmithyBuild") +tasks["assemble"].finalizedBy("generateCargoWorkspace") + +project.registerModifyMtimeTask() +project.registerCargoCommandsTasks(buildDir.resolve(workingDirUnderBuildDir), defaultRustDocFlags) + +tasks["test"].finalizedBy(cargoCommands(properties).map { it.toString }) + +tasks["clean"].doFirst { delete("smithy-build.json") } diff --git a/codegen-server-test/python/model/pokemon-common.smithy b/codegen-server-test/typescript/model/pokemon-common.smithy similarity index 100% rename from codegen-server-test/python/model/pokemon-common.smithy rename to codegen-server-test/typescript/model/pokemon-common.smithy diff --git a/codegen-server-test/typescript/model/pokemon.smithy b/codegen-server-test/typescript/model/pokemon.smithy new file mode 100644 index 0000000000..06e46906e7 --- /dev/null +++ b/codegen-server-test/typescript/model/pokemon.smithy @@ -0,0 +1,25 @@ +/// TODO(https://github.com/awslabs/smithy-rs/issues/1508) +/// reconcile this model with the main one living inside codegen-server-test/model/pokemon.smithy +/// once the Typescript implementation supports Streaming and Union shapes. +$version: "1.0" + +namespace com.aws.example.ts + +use aws.protocols#restJson1 +use com.aws.example#PokemonSpecies +use com.aws.example#GetServerStatistics +use com.aws.example#DoNothing +use com.aws.example#CheckHealth + +/// The Pokémon Service allows you to retrieve information about Pokémon species. +@title("Pokémon Service") +@restJson1 +service PokemonService { + version: "2021-12-01", + resources: [PokemonSpecies], + operations: [ + GetServerStatistics, + DoNothing, + CheckHealth, + ], +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonEventStreamSymbolProvider.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonEventStreamSymbolProvider.kt new file mode 100644 index 0000000000..38d3830bac --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonEventStreamSymbolProvider.kt @@ -0,0 +1,104 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.isEventStream +import software.amazon.smithy.rust.codegen.core.util.isOutputEventStream +import software.amazon.smithy.rust.codegen.core.util.toPascalCase + +/** + * Symbol provider for Python that maps event streaming member shapes to their respective Python wrapper types. + * + * For example given a model: + * ```smithy + * @input + * structure CapturePokemonInput { + * @httpPayload + * events: AttemptCapturingPokemonEvent, + * } + * + * @streaming + * union AttemptCapturingPokemonEvent { + * ... + * } + * ``` + * for the member shape `CapturePokemonInput$events` it will return a symbol that points to + * `crate::python_event_stream::CapturePokemonInputEventsReceiver`. + */ +class PythonEventStreamSymbolProvider( + private val runtimeConfig: RuntimeConfig, + base: RustSymbolProvider, +) : WrappingSymbolProvider(base) { + override fun toSymbol(shape: Shape): Symbol { + val initial = super.toSymbol(shape) + + // We only want to wrap with Event Stream types when dealing with member shapes + if (shape !is MemberShape || !shape.isEventStream(model)) { + return initial + } + + // We can only wrap the type if it's either an input or an output that used in an operation + model.expectShape(shape.container).let { maybeInputOutput -> + val operationId = maybeInputOutput.getTrait()?.operation + ?: maybeInputOutput.getTrait()?.operation + operationId?.let { model.expectShape(it, OperationShape::class.java) } + } ?: return initial + + val unionShape = model.expectShape(shape.target).asUnionShape().get() + val error = if (unionShape.eventStreamErrors().isEmpty()) { + RuntimeType.smithyHttp(runtimeConfig).resolve("event_stream::MessageStreamError").toSymbol() + } else { + symbolForEventStreamError(unionShape) + } + val inner = initial.rustType().stripOuter() + val innerSymbol = Symbol.builder().name(inner.name).rustType(inner).build() + val containerName = shape.container.name + val memberName = shape.memberName.toPascalCase() + val outer = when (shape.isOutputEventStream(model)) { + true -> "${containerName}${memberName}EventStreamSender" + else -> "${containerName}${memberName}Receiver" + } + val rustType = RustType.Opaque(outer, PythonServerRustModule.PythonEventStream.fullyQualifiedPath()) + return Symbol.builder() + .name(rustType.name) + .rustType(rustType) + .addReference(innerSymbol) + .addReference(error) + .addDependency(CargoDependency.smithyHttp(runtimeConfig).withFeature("event-stream")) + .addDependency(PythonServerCargoDependency.TokioStream) + .addDependency(PythonServerCargoDependency.PyO3Asyncio.withFeature("unstable-streams")) + .build() + } + + companion object { + data class EventStreamSymbol(val innerT: RustType, val errorT: RustType) + + fun parseSymbol(symbol: Symbol): EventStreamSymbol { + check(symbol.references.size >= 2) { + "`PythonEventStreamSymbolProvider` adds inner type and error type as references to resulting symbol" + } + val innerT = symbol.references[0].symbol.rustType() + val errorT = symbol.references[1].symbol.rustType() + return EventStreamSymbol(innerT, errorT) + } + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt index d1ad482f2a..2782e44b94 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt @@ -15,9 +15,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig * For a dependency that is used in the client, or in both the client and the server, use [CargoDependency] directly. */ object PythonServerCargoDependency { - val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.17")) - val PyO3Asyncio: CargoDependency = CargoDependency("pyo3-asyncio", CratesIo("0.17"), features = setOf("attributes", "tokio-runtime")) + val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.18")) + val PyO3Asyncio: CargoDependency = CargoDependency("pyo3-asyncio", CratesIo("0.18"), features = setOf("attributes", "tokio-runtime")) val Tokio: CargoDependency = CargoDependency("tokio", CratesIo("1.20.1"), features = setOf("full")) + val TokioStream: CargoDependency = CargoDependency("tokio-stream", CratesIo("0.1.12")) val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) val Tower: CargoDependency = CargoDependency("tower", CratesIo("0.4")) val TowerHttp: CargoDependency = CargoDependency("tower-http", CratesIo("0.3"), features = setOf("trace")) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 216f21a4f5..3bb954bfc2 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -8,6 +8,8 @@ package software.amazon.smithy.rust.codegen.server.python.smithy import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.NullableIndex +import software.amazon.smithy.model.shapes.BlobShape +import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StringShape @@ -21,12 +23,16 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.isEventStream +import software.amazon.smithy.rust.codegen.server.python.smithy.generators.ConstrainedPythonBlobGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonApplicationGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator +import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEventStreamErrorGenerator +import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEventStreamWrapperGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerOperationErrorGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerOperationHandlerGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerStructureGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerUnionGenerator +import software.amazon.smithy.rust.codegen.server.python.smithy.protocols.PythonServerProtocolLoader import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenVisitor import software.amazon.smithy.rust.codegen.server.smithy.ServerModuleDocProvider @@ -37,11 +43,11 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerSymbolProviders import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.createInlineModuleCreator import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator -import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationErrorGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol -import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader +import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput +import software.amazon.smithy.rust.codegen.server.smithy.withModuleOrWithStructureBuilderModule /** * Entrypoint for Python server-side code generation. This class will walk the in-memory model and @@ -66,16 +72,16 @@ class PythonServerCodegenVisitor( val baseModel = baselineTransform(context.model) val service = settings.getService(baseModel) val (protocol, generator) = - ServerProtocolLoader( + PythonServerProtocolLoader( codegenDecorator.protocols( service.id, - ServerProtocolLoader.DefaultProtocols, + PythonServerProtocolLoader.defaultProtocols(settings.runtimeConfig), ), ) .protocolFor(context.model, service) protocolGeneratorFactory = generator - model = codegenDecorator.transformModel(service, baseModel) + model = codegenDecorator.transformModel(service, baseModel, settings) // `publicConstrainedTypes` must always be `false` for the Python server, since Python generates its own // wrapper newtypes. @@ -146,7 +152,7 @@ class PythonServerCodegenVisitor( rustCrate.useShapeWriter(shape) { // Use Python specific structure generator that adds the #[pyclass] attribute // and #[pymethods] implementation. - PythonServerStructureGenerator(model, codegenContext.symbolProvider, this, shape).render() + PythonServerStructureGenerator(model, codegenContext, this, shape).render() shape.getTrait()?.also { errorTrait -> ErrorImplGenerator( @@ -184,7 +190,7 @@ class PythonServerCodegenVisitor( override fun unionShape(shape: UnionShape) { logger.info("[python-server-codegen] Generating an union shape $shape") rustCrate.useShapeWriter(shape) { - PythonServerUnionGenerator(model, codegenContext.symbolProvider, this, shape, renderUnknownVariant = false).render() + PythonServerUnionGenerator(model, codegenContext, this, shape, renderUnknownVariant = false).render() } if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( @@ -205,7 +211,7 @@ class PythonServerCodegenVisitor( if (shape.isEventStream()) { rustCrate.withModule(ServerRustModule.Error) { - ServerOperationErrorGenerator(model, codegenContext.symbolProvider, shape).render(this) + PythonServerEventStreamErrorGenerator(model, codegenContext.symbolProvider, shape).render(this) } } } @@ -246,4 +252,31 @@ class PythonServerCodegenVisitor( PythonServerOperationErrorGenerator(codegenContext.model, codegenContext.symbolProvider, shape).render(this) } } + + override fun memberShape(shape: MemberShape) { + super.memberShape(shape) + + if (shape.isEventStream(model)) { + rustCrate.withModule(PythonServerRustModule.PythonEventStream) { + PythonServerEventStreamWrapperGenerator(codegenContext, shape).render(this) + } + } + } + + override fun blobShape(shape: BlobShape) { + logger.info("[python-server-codegen] Generating a service $shape") + super.blobShape(shape) + + if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { + ConstrainedPythonBlobGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + this, + shape, + validationExceptionConversionGenerator, + ).render() + } + } + } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt index c5438b4a9f..9df4937579 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt @@ -15,6 +15,7 @@ object PythonServerRustModule { val PythonModuleExport = RustModule.public("python_module_export") val PythonOperationAdapter = RustModule.public("python_operation_adaptor") val PythonServerApplication = RustModule.public("python_server_application") + val PythonEventStream = RustModule.public("python_event_stream") } class PythonServerModuleDocProvider(private val base: ModuleDocProvider) : ModuleDocProvider { @@ -23,8 +24,8 @@ class PythonServerModuleDocProvider(private val base: ModuleDocProvider) : Modul return when (module) { PythonServerRustModule.PythonModuleExport -> strDoc("Export PyO3 symbols in the shared library") PythonServerRustModule.PythonServerApplication -> strDoc("Python server and application implementation.") - // TODO(ServerTeam): Document this module (I don't have context) - PythonServerRustModule.PythonOperationAdapter -> null + PythonServerRustModule.PythonOperationAdapter -> strDoc("Operation adapters that delegate to Python handlers.") + PythonServerRustModule.PythonEventStream -> strDoc("Python wrapper types for event streams.") else -> base.docsWriter(module) } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt index c5606b5d38..8cdc481b39 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt @@ -90,45 +90,63 @@ sealed class PythonType { override val namespace: String = "typing" } - data class Opaque(override val name: String, val rustNamespace: String? = null) : PythonType() { - // Since Python doesn't have a something like Rust's `crate::` we are using a custom placeholder here - // and in our stub generation script we will replace placeholder with the real root module name. - private val pythonRootModulePlaceholder = "__root_module_name__" + data class AsyncIterator(override val member: PythonType) : PythonType(), Container { + override val name: String = "AsyncIterator" + override val namespace: String = "typing" + } + + data class Application(val type: PythonType, val args: kotlin.collections.List) : PythonType() { + override val name = type.name + override val namespace = type.namespace + } + + data class Opaque(override val name: String, val pythonRootModuleName: String, val rustNamespace: String? = null) : PythonType() { override val namespace: String? = rustNamespace?.split("::")?.joinToString(".") { when (it) { - "crate" -> pythonRootModulePlaceholder + "crate" -> pythonRootModuleName // In Python, we expose submodules from `aws_smithy_http_server_python` - // like `types`, `middleware`, `tls` etc. from `__root_module__name` - "aws_smithy_http_server_python" -> pythonRootModulePlaceholder + // like `types`, `middleware`, `tls` etc. from Python root module + "aws_smithy_http_server_python" -> pythonRootModuleName else -> it } } + // Most opaque types have a leading `::`, so strip that for Python as needed + .let { + when (it?.startsWith(".")) { + true -> it.substring(1) + else -> it + } + } } } /** * Return corresponding [PythonType] for a [RustType]. */ -fun RustType.pythonType(): PythonType = +fun RustType.pythonType(pythonRootModuleName: String): PythonType = when (this) { is RustType.Unit -> PythonType.None is RustType.Bool -> PythonType.Bool is RustType.Float -> PythonType.Float is RustType.Integer -> PythonType.Int is RustType.String -> PythonType.Str - is RustType.Vec -> PythonType.List(this.member.pythonType()) - is RustType.Slice -> PythonType.List(this.member.pythonType()) - is RustType.HashMap -> PythonType.Dict(this.key.pythonType(), this.member.pythonType()) - is RustType.HashSet -> PythonType.Set(this.member.pythonType()) - is RustType.Reference -> this.member.pythonType() - is RustType.Option -> PythonType.Optional(this.member.pythonType()) - is RustType.Box -> this.member.pythonType() - is RustType.Dyn -> this.member.pythonType() - is RustType.Opaque -> PythonType.Opaque(this.name, this.namespace) - // TODO(Constraints): How to handle this? - // Revisit as part of https://github.com/awslabs/smithy-rs/issues/2114 - is RustType.MaybeConstrained -> this.member.pythonType() + is RustType.Vec -> PythonType.List(this.member.pythonType(pythonRootModuleName)) + is RustType.Slice -> PythonType.List(this.member.pythonType(pythonRootModuleName)) + is RustType.HashMap -> PythonType.Dict(this.key.pythonType(pythonRootModuleName), this.member.pythonType(pythonRootModuleName)) + is RustType.HashSet -> PythonType.Set(this.member.pythonType(pythonRootModuleName)) + is RustType.Reference -> this.member.pythonType(pythonRootModuleName) + is RustType.Option -> PythonType.Optional(this.member.pythonType(pythonRootModuleName)) + is RustType.Box -> this.member.pythonType(pythonRootModuleName) + is RustType.Dyn -> this.member.pythonType(pythonRootModuleName) + is RustType.Application -> PythonType.Application( + this.type.pythonType(pythonRootModuleName), + this.args.map { + it.pythonType(pythonRootModuleName) + }, + ) + is RustType.Opaque -> PythonType.Opaque(this.name, pythonRootModuleName, rustNamespace = this.namespace) + is RustType.MaybeConstrained -> this.member.pythonType(pythonRootModuleName) } /** @@ -154,6 +172,11 @@ fun PythonType.render(fullyQualified: Boolean = true): String { is PythonType.Set -> "${this.name}[${this.member.render(fullyQualified)}]" is PythonType.Awaitable -> "${this.name}[${this.member.render(fullyQualified)}]" is PythonType.Optional -> "${this.name}[${this.member.render(fullyQualified)}]" + is PythonType.AsyncIterator -> "${this.name}[${this.member.render(fullyQualified)}]" + is PythonType.Application -> { + val args = this.args.joinToString(", ") { it.render(fullyQualified) } + "${this.name}[$args]" + } is PythonType.Callable -> { val args = this.args.joinToString(", ") { it.render(fullyQualified) } val rtype = this.rtype.render(fullyQualified) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt index faf01b1fcf..a2c9ab45de 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt @@ -12,8 +12,6 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.BaseSymbolMetadataProvider -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.EventStreamSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig import software.amazon.smithy.rust.codegen.server.python.smithy.customizations.DECORATORS import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolMetadataProvider @@ -88,7 +86,7 @@ class RustServerCodegenPythonPlugin : SmithyBuildPlugin { if (includeConstrainedShapeProvider) PythonConstrainedShapeSymbolProvider(it, serviceShape, constrainedTypes) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) - .let { EventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it, CodegenTarget.SERVER) } + .let { PythonEventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes .let { BaseSymbolMetadataProvider(it, additionalAttributes = listOf()) } // Constrained shapes generate newtypes that need the same derives we place on types generated from aggregate shapes. diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt index 9e353db2cd..daa4a7b4dd 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt @@ -194,6 +194,28 @@ class PyTypedMarkerDecorator : ServerCodegenDecorator { } } +/** + * Copies the stubgen scripts to the generated crate root. + * + * The shell script `stubgen.sh` runs a quick build and uses `stubgen.py` to generate mypy compatibile + * types stubs for the project. + */ +class AddStubgenScriptDecorator : ServerCodegenDecorator { + override val name: String = "AddStubgenScriptDecorator" + override val order: Byte = 0 + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + val stubgenPythonContent = this::class.java.getResource("/stubgen.py").readText() + rustCrate.withFile("stubgen.py") { + writeWithNoFormatting("$stubgenPythonContent") + } + val stubgenShellContent = this::class.java.getResource("/stubgen.sh").readText() + rustCrate.withFile("stubgen.sh") { + writeWithNoFormatting("$stubgenShellContent") + } + } +} + val DECORATORS = arrayOf( /** * Add the [InternalServerError] error to all operations. @@ -214,4 +236,6 @@ val DECORATORS = arrayOf( InitPyDecorator(), // Generate `py.typed` for the Python source. PyTypedMarkerDecorator(), + // Generate scripts for stub generation. + AddStubgenScriptDecorator(), ) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/ConstrainedPythonBlobGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/ConstrainedPythonBlobGenerator.kt new file mode 100644 index 0000000000..a9c2021520 --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/ConstrainedPythonBlobGenerator.kt @@ -0,0 +1,100 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.generators + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.BlobShape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.join +import software.amazon.smithy.rust.codegen.core.rustlang.render +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.InlineModuleCreator +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.generators.BlobLength +import software.amazon.smithy.rust.codegen.server.smithy.generators.TraitInfo +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator + +class ConstrainedPythonBlobGenerator( + val codegenContext: ServerCodegenContext, + private val inlineModuleCreator: InlineModuleCreator, + val writer: RustWriter, + val shape: BlobShape, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) { + val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider + val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with(codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) + private val blobConstraintsInfo: List = listOf(LengthTrait::class.java) + .mapNotNull { shape.getTrait(it).orNull() } + .map { BlobLength(it) } + private val constraintsInfo: List = blobConstraintsInfo.map { it.toTraitInfo() } + + fun render() { + val symbol = constrainedShapeSymbolProvider.toSymbol(shape) + val blobType = PythonServerRuntimeType.blob(codegenContext.runtimeConfig).toSymbol().rustType() + renderFrom(symbol, blobType) + renderTryFrom(symbol, blobType) + } + + fun renderFrom(symbol: Symbol, blobType: RustType) { + val name = symbol.name + val inner = blobType.render() + writer.rustTemplate( + """ + impl #{From}<$inner> for #{MaybeConstrained} { + fn from(value: $inner) -> Self { + Self::Unconstrained(value.into()) + } + } + + impl #{From}<$name> for $inner { + fn from(value: $name) -> Self { + value.into_inner().into() + } + } + """, + "MaybeConstrained" to symbol.makeMaybeConstrained(), + "From" to RuntimeType.From, + ) + } + + fun renderTryFrom(symbol: Symbol, blobType: RustType) { + val name = symbol.name + val inner = blobType.render() + writer.rustTemplate( + """ + impl #{TryFrom}<$inner> for $name { + type Error = #{ConstraintViolation}; + + fn try_from(value: $inner) -> Result { + value.try_into() + } + } + """, + "TryFrom" to RuntimeType.TryFrom, + "ConstraintViolation" to constraintViolation, + "TryFromChecks" to constraintsInfo.map { it.tryFromCheck }.join("\n"), + ) + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt index cc4804bb21..96ecbfe75c 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.python.smithy.generators import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.DocumentationTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate @@ -203,14 +204,13 @@ class PythonApplicationGenerator( *codegenScope, ) for (operation in operations) { - val operationName = symbolProvider.toSymbol(operation).name - val name = operationName.toSnakeCase() + val fnName = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operation).name.toSnakeCase()) rustTemplate( """ - let ${name}_locals = #{pyo3_asyncio}::TaskLocals::new(event_loop); - let handler = self.handlers.get("$name").expect("Python handler for operation `$name` not found").clone(); - let builder = builder.$name(move |input, state| { - #{pyo3_asyncio}::tokio::scope(${name}_locals.clone(), crate::python_operation_adaptor::$name(input, state, handler.clone())) + let ${fnName}_locals = #{pyo3_asyncio}::TaskLocals::new(event_loop); + let handler = self.handlers.get("$fnName").expect("Python handler for operation `$fnName` not found").clone(); + let builder = builder.$fnName(move |input, state| { + #{pyo3_asyncio}::tokio::scope(${fnName}_locals.clone(), crate::python_operation_adaptor::$fnName(input, state, handler.clone())) }); """, *codegenScope, @@ -250,11 +250,11 @@ class PythonApplicationGenerator( """, *codegenScope, ) { - val middlewareRequest = PythonType.Opaque("Request", "crate::middleware") - val middlewareResponse = PythonType.Opaque("Response", "crate::middleware") + val middlewareRequest = PythonType.Opaque("Request", libName, rustNamespace = "crate::middleware") + val middlewareResponse = PythonType.Opaque("Response", libName, rustNamespace = "crate::middleware") val middlewareNext = PythonType.Callable(listOf(middlewareRequest), PythonType.Awaitable(middlewareResponse)) val middlewareFunc = PythonType.Callable(listOf(middlewareRequest, middlewareNext), PythonType.Awaitable(middlewareResponse)) - val tlsConfig = PythonType.Opaque("TlsConfig", "crate::tls") + val tlsConfig = PythonType.Opaque("TlsConfig", libName, rustNamespace = "crate::tls") rustTemplate( """ @@ -342,11 +342,11 @@ class PythonApplicationGenerator( ) operations.map { operation -> val operationName = symbolProvider.toSymbol(operation).name - val name = operationName.toSnakeCase() + val fnName = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operation).name.toSnakeCase()) - val input = PythonType.Opaque("${operationName}Input", "crate::input") - val output = PythonType.Opaque("${operationName}Output", "crate::output") - val context = PythonType.Opaque("Ctx") + val input = PythonType.Opaque("${operationName}Input", libName, rustNamespace = "crate::input") + val output = PythonType.Opaque("${operationName}Output", libName, rustNamespace = "crate::output") + val context = PythonType.Opaque("Ctx", libName) val returnType = PythonType.Union(listOf(output, PythonType.Awaitable(output))) val handler = PythonType.Union( listOf( @@ -363,15 +363,15 @@ class PythonApplicationGenerator( rustTemplate( """ - /// Method to register `$name` Python implementation inside the handlers map. + /// Method to register `$fnName` Python implementation inside the handlers map. /// It can be used as a function decorator in Python. /// /// :param func ${handler.renderAsDocstring()}: /// :rtype ${PythonType.None.renderAsDocstring()}: ##[pyo3(text_signature = "(${'$'}self, func)")] - pub fn $name(&mut self, py: #{pyo3}::Python, func: #{pyo3}::PyObject) -> #{pyo3}::PyResult<()> { + pub fn $fnName(&mut self, py: #{pyo3}::Python, func: #{pyo3}::PyObject) -> #{pyo3}::PyResult<()> { use #{SmithyPython}::PyApp; - self.register_operation(py, "$name", func) + self.register_operation(py, "$fnName", func) } """, *codegenScope, diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt new file mode 100644 index 0000000000..b915e953cd --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationErrorGenerator + +/** + * Generates Python compatible error types for event streaming union types. + * It just uses [ServerOperationErrorGenerator] under the hood to generate pure Rust error types and then adds + * some implementation to errors to make it possible to moving errors from Rust to Python and vice-versa. + * It adds following implementations to errors: + * - `pyo3::FromPyObject`: To allow extracting Rust errors from Python errors + * - `pyo3::IntoPy`: To allow converting Rust errors to Python errors + * - `impl From<#{Error}> for pyo3::PyErr`: To allow converting Rust errors to Python exceptions, + * it uses previous impl to convert errors + */ +class PythonServerEventStreamErrorGenerator( + private val model: Model, + private val symbolProvider: RustSymbolProvider, + val shape: UnionShape, +) : ServerOperationErrorGenerator( + model, + symbolProvider, + shape, +) { + private val errorSymbol = symbolProvider.symbolForEventStreamError(shape) + private val errors = shape.eventStreamErrors().map { + model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) + } + + private val pyO3 = PythonServerCargoDependency.PyO3.toType() + + override fun render(writer: RustWriter) { + super.render(writer) + renderFromPyObjectImpl(writer) + renderIntoPyImpl(writer) + renderIntoPyErrImpl(writer) + } + + private fun renderFromPyObjectImpl(writer: RustWriter) { + writer.rustBlockTemplate("impl<'source> #{PyO3}::FromPyObject<'source> for ${errorSymbol.name}", "PyO3" to pyO3) { + writer.rustBlockTemplate("fn extract(obj: &'source #{PyO3}::PyAny) -> #{PyO3}::PyResult", "PyO3" to pyO3) { + errors.forEach { + val symbol = symbolProvider.toSymbol(it) + writer.rust( + """ + if let Ok(it) = obj.extract::<#T>() { + return Ok(Self::${symbol.name}(it)); + } + """, + symbol, + ) + } + writer.rustTemplate( + """ + Err(#{PyO3}::exceptions::PyTypeError::new_err( + format!( + "failed to extract '${errorSymbol.name}' from '{}'", + obj + ) + )) + """, + "PyO3" to pyO3, + ) + } + } + } + + private fun renderIntoPyImpl(writer: RustWriter) { + writer.rustBlockTemplate("impl #{PyO3}::IntoPy<#{PyO3}::PyObject> for ${errorSymbol.name}", "PyO3" to pyO3) { + writer.rustBlockTemplate("fn into_py(self, py: #{PyO3}::Python<'_>) -> #{PyO3}::PyObject", "PyO3" to pyO3) { + writer.rustBlock("match self") { + errors.forEach { + val symbol = symbolProvider.toSymbol(it) + writer.rustTemplate( + """ + Self::${symbol.name}(it) => match #{PyO3}::Py::new(py, it) { + Ok(it) => it.into_py(py), + Err(err) => err.into_py(py), + } + """, + "PyO3" to pyO3, + ) + } + } + } + } + } + + private fun renderIntoPyErrImpl(writer: RustWriter) { + writer.rustBlockTemplate("impl #{From}<${errorSymbol.name}> for #{PyO3}::PyErr", "PyO3" to pyO3, "From" to RuntimeType.From) { + writer.rustBlockTemplate("fn from(err: ${errorSymbol.name}) -> #{PyO3}::PyErr", "PyO3" to pyO3) { + writer.rustTemplate( + """ + #{PyO3}::Python::with_gil(|py| { + let py_err = #{PyO3}::IntoPy::into_py(err, py); + #{PyO3}::PyErr::from_value(py_err.as_ref(py)) + }) + """, + "PyO3" to pyO3, + ) + } + } + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt new file mode 100644 index 0000000000..8594d703c1 --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt @@ -0,0 +1,241 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.generators + +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.util.isOutputEventStream +import software.amazon.smithy.rust.codegen.core.util.toPascalCase +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonEventStreamSymbolProvider +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency + +/** + * Generates Python wrapper types for event streaming members. + * In pure Rust we use `aws_smithy_http::event_stream::{Receiver,EventStreamSender}` for event streaming members, + * this is not viable for Python because PyO3 expects following for a type to be exposed to Python, but we fail to satisfy: + * - It should be `Clone` and `Send` + * - It shouldn't have any generic parameters + * + * So we generate wrapper types for every streaming member, that looks like: + * ```rust + * #[pyo3::pyclass] + * #[derive(std::clone::Clone, std::fmt::Debug)] + * pub struct CapturePokemonInputEventsReceiver { + * // Arc makes it cloneable + * inner: std::sync::Arc< + * // Mutex makes it sendable + * tokio::sync::Mutex< + * // Filling generic args with specific impls make outer type non-generic + * aws_smithy_http::event_stream::Receiver< + * crate::model::AttemptCapturingPokemonEvent, + * crate::error::AttemptCapturingPokemonEventError, + * >, + * >, + * >, + * } + * ``` + * + * For Receiver and Sender types we also implement: + * Receiver: `__anext__` so it can be used in async for loops in Python + * Sender: `FromPyObject` that converts an async Python generator to a Rust stream + */ +class PythonServerEventStreamWrapperGenerator( + codegenContext: CodegenContext, + private val shape: MemberShape, +) { + private val runtimeConfig = codegenContext.runtimeConfig + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + + private val symbol = symbolProvider.toSymbol(shape) + private val eventStreamSymbol = PythonEventStreamSymbolProvider.parseSymbol(symbol) + private val innerT = eventStreamSymbol.innerT + private val errorT = eventStreamSymbol.errorT + private val containerName = shape.container.name + private val memberName = shape.memberName.toPascalCase() + + private val pyO3 = PythonServerCargoDependency.PyO3.toType() + private val codegenScope = + arrayOf( + "Inner" to innerT, + "Error" to errorT, + "SmithyPython" to PythonServerCargoDependency.smithyHttpServerPython(runtimeConfig).toType(), + "SmithyHttp" to RuntimeType.smithyHttp(runtimeConfig), + "Tracing" to PythonServerCargoDependency.Tracing.toType(), + "PyO3" to pyO3, + "PyO3Asyncio" to PythonServerCargoDependency.PyO3Asyncio.toType(), + "TokioStream" to PythonServerCargoDependency.TokioStream.toType(), + "Mutex" to PythonServerCargoDependency.ParkingLot.toType().resolve("Mutex"), + "AsyncMutex" to PythonServerCargoDependency.Tokio.toType().resolve("sync::Mutex"), + "Send" to RuntimeType.Send, + "Sync" to RuntimeType.Sync, + "Option" to RuntimeType.Option, + "Arc" to RuntimeType.Arc, + "Body" to RuntimeType.sdkBody(runtimeConfig), + "UnmarshallMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::UnmarshallMessage"), + "MarshallMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::MarshallMessage"), + "SignMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::SignMessage"), + "MessageStreamAdapter" to RuntimeType.smithyHttp(runtimeConfig).resolve("event_stream::MessageStreamAdapter"), + ) + + fun render(writer: RustWriter) { + if (shape.isOutputEventStream(model)) { + renderSender(writer) + } else { + renderReceiver(writer) + } + } + + private fun renderSender(writer: RustWriter) { + val name = "${containerName}${memberName}EventStreamSender" + val wrappedT = RuntimeType.eventStreamSender(runtimeConfig) + val containerMeta = symbol.expectRustMetadata().withDerives(RuntimeType.Clone, RuntimeType.Debug) + containerMeta.render(writer) + writer.rustBlock("struct $name") { + writer.rustTemplate( + "inner: #{Arc}<#{Mutex}<#{Option}<#{Wrapped}<#{Inner}, #{Error}>>>>", + *codegenScope, + "Wrapped" to wrappedT, + ) + } + + writer.rustBlock("impl $name") { + writer.rustTemplate( + """ + pub fn into_body_stream( + self, + marshaller: impl #{MarshallMessage} + #{Send} + #{Sync} + 'static, + error_marshaller: impl #{MarshallMessage} + #{Send} + #{Sync} + 'static, + signer: impl #{SignMessage} + #{Send} + #{Sync} + 'static, + ) -> #{MessageStreamAdapter}<#{Inner}, #{Error}> { + let mut inner = self.inner.lock(); + let inner = inner.take().expect( + "attempted to reuse an event stream. \ + that means you kept a reference to an event stream and tried to reuse it in another request, \ + event streams are request scoped and shouldn't be used outside of their bounded request scope" + ); + inner.into_body_stream(marshaller, error_marshaller, signer) + } + """, + *codegenScope, + "Wrapped" to wrappedT, + ) + } + + writer.rustTemplate( + """ + impl<'source> #{PyO3}::FromPyObject<'source> for $name { + fn extract(obj: &'source #{PyO3}::PyAny) -> #{PyO3}::PyResult { + use #{TokioStream}::StreamExt; + let stream = #{PyO3Asyncio}::tokio::into_stream_v1(obj)?; + let stream = stream.filter_map(|res| { + #{PyO3}::Python::with_gil(|py| { + // TODO(EventStreamImprovements): Add `InternalServerError` variant to all event streaming + // errors and return that variant in case of errors here? + match res { + Ok(obj) => { + match obj.extract::<#{Inner}>(py) { + Ok(it) => Some(Ok(it)), + Err(err) => { + let rich_py_err = #{SmithyPython}::rich_py_err(err); + #{Tracing}::error!(error = ?rich_py_err, "could not extract the output type '#{Inner}' from streamed value"); + None + }, + } + }, + Err(err) => { + match #{PyO3}::IntoPy::into_py(err, py).extract::<#{Error}>(py) { + Ok(modelled_error) => Some(Err(modelled_error)), + Err(err) => { + let rich_py_err = #{SmithyPython}::rich_py_err(err); + #{Tracing}::error!(error = ?rich_py_err, "could not extract the error type '#{Error}' from raised exception"); + None + } + } + } + } + }) + }); + + Ok($name { inner: #{Arc}::new(#{Mutex}::new(Some(stream.into()))) }) + } + } + + impl #{PyO3}::IntoPy<#{PyO3}::PyObject> for $name { + fn into_py(self, py: #{PyO3}::Python<'_>) -> #{PyO3}::PyObject { + #{PyO3}::exceptions::PyAttributeError::new_err("this is a write-only object").into_py(py) + } + } + """, + *codegenScope, + ) + } + + private fun renderReceiver(writer: RustWriter) { + val name = "${containerName}${memberName}Receiver" + val wrappedT = RuntimeType.eventStreamReceiver(runtimeConfig) + val containerMeta = symbol.expectRustMetadata().withDerives(RuntimeType.Clone, RuntimeType.Debug) + Attribute(pyO3.resolve("pyclass")).render(writer) + containerMeta.render(writer) + writer.rustBlock("struct $name") { + writer.rustTemplate( + "inner: #{Arc}<#{AsyncMutex}<#{Wrapped}<#{Inner}, #{Error}>>>", + *codegenScope, + "Wrapped" to wrappedT, + ) + } + + writer.rustBlock("impl $name") { + writer.rustTemplate( + """ + pub fn new( + unmarshaller: impl #{UnmarshallMessage} + #{Send} + #{Sync} + 'static, + body: #{Body} + ) -> $name { + let inner = #{Wrapped}::new(unmarshaller, body); + let inner = #{Arc}::new(#{AsyncMutex}::new(inner)); + $name { inner } + } + """, + *codegenScope, + "Wrapped" to wrappedT, + ) + } + + Attribute(pyO3.resolve("pymethods")).render(writer) + writer.rustBlock("impl $name") { + writer.rustTemplate( + """ + pub fn __aiter__(slf: #{PyO3}::PyRef) -> #{PyO3}::PyRef { + slf + } + + pub fn __anext__(slf: #{PyO3}::PyRefMut) -> #{PyO3}::PyResult> { + let body = slf.inner.clone(); + let fut = #{PyO3Asyncio}::tokio::future_into_py(slf.py(), async move { + let mut inner = body.lock().await; + let next = inner.recv().await; + match next { + Ok(Some(data)) => Ok(#{PyO3}::Python::with_gil(|py| #{PyO3}::IntoPy::into_py(data, py))), + Ok(None) => Err(#{PyO3}::exceptions::PyStopAsyncIteration::new_err("stream exhausted")), + Err(#{SmithyHttp}::result::SdkError::ServiceError(service_err)) => Err(service_err.into_err().into()), + Err(err) => Err(#{PyO3}::exceptions::PyRuntimeError::new_err(err.to_string())), + } + })?; + Ok(Some(fut.into())) + } + """, + *codegenScope, + ) + } + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt index 8a622c51ef..577999a724 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt @@ -11,10 +11,13 @@ import software.amazon.smithy.model.shapes.ResourceShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.core.Version import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerRustModule @@ -50,6 +53,8 @@ class PythonServerModuleGenerator( renderPyTlsTypes() renderPyLambdaTypes() renderPyApplicationType() + renderCodegenVersion() + rust("Ok(())") } } } @@ -61,23 +66,33 @@ class PythonServerModuleGenerator( let input = #{pyo3}::types::PyModule::new(py, "input")?; let output = #{pyo3}::types::PyModule::new(py, "output")?; let error = #{pyo3}::types::PyModule::new(py, "error")?; - let model = #{pyo3}::types::PyModule::new(py, "model")?; """, *codegenScope, ) + // The `model` type section can be unused in models like `simple`, so we accommodate for it. + var visitedModelType = false serviceShapes.forEach { shape -> val moduleType = moduleType(shape) if (moduleType != null) { + if (moduleType == "model" && !visitedModelType) { + rustTemplate( + """ + let model = #{pyo3}::types::PyModule::new(py, "model")?; + """, + *codegenScope, + ) + visitedModelType = true + } when (shape) { is UnionShape -> rustTemplate( """ - $moduleType.add_class::()?; + $moduleType.add_class::()?; """, *codegenScope, ) else -> rustTemplate( """ - $moduleType.add_class::()?; + $moduleType.add_class::()?; """, *codegenScope, ) @@ -92,11 +107,18 @@ class PythonServerModuleGenerator( m.add_submodule(output)?; #{pyo3}::py_run!(py, error, "import sys; sys.modules['$libName.error'] = error"); m.add_submodule(error)?; - #{pyo3}::py_run!(py, model, "import sys; sys.modules['$libName.model'] = model"); - m.add_submodule(model)?; """, *codegenScope, ) + if (visitedModelType) { + rustTemplate( + """ + #{pyo3}::py_run!(py, model, "import sys; sys.modules['$libName.model'] = model"); + m.add_submodule(model)?; + """, + *codegenScope, + ) + } } // Render wrapper types that are substituted to the ones coming from `aws_smithy_types`. @@ -210,13 +232,12 @@ class PythonServerModuleGenerator( // Render Python application type. private fun RustWriter.renderPyApplicationType() { - rustTemplate( - """ - m.add_class::()?; - Ok(()) - """, - *codegenScope, - ) + rust("""m.add_class::()?;""") + } + + // Render the codegeneration version as module attribute. + private fun RustWriter.renderCodegenVersion() { + rust("""m.add("CODEGEN_VERSION", "${Version.crateVersion()}")?;""") } // Convert to symbol and check the namespace to figure out where they should be imported from. diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt index f107806098..d9ab006b0d 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt @@ -6,11 +6,13 @@ package software.amazon.smithy.rust.codegen.server.python.smithy.generators import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency @@ -50,10 +52,11 @@ class PythonServerOperationHandlerGenerator( private fun renderPythonOperationHandlerImpl(writer: RustWriter) { val operationName = symbolProvider.toSymbol(operation).name - val input = "crate::input::${operationName}Input" - val output = "crate::output::${operationName}Output" + val input = "crate::input::${operationName.toPascalCase()}Input" + val output = "crate::output::${operationName.toPascalCase()}Output" + // TODO(https://github.com/awslabs/smithy-rs/issues/2552) - Use to pascalCase for error shapes. val error = "crate::error::${operationName}Error" - val fnName = operationName.toSnakeCase() + val fnName = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operation).name.toSnakeCase()) writer.rustTemplate( """ @@ -112,7 +115,7 @@ class PythonServerOperationHandlerGenerator( }; #{pyo3_asyncio}::tokio::into_future(coroutine) })?; - result.await.map(|r| #{pyo3}::Python::with_gil(|py| r.extract::<$output>(py)))? + result.await.and_then(|r| #{pyo3}::Python::with_gil(|py| r.extract::<$output>(py))) """, *codegenScope, ) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt index 5f226267f6..d9d2df8cc2 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt @@ -18,14 +18,17 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustInlineTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.isEventStream +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonEventStreamSymbolProvider import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.python.smithy.PythonType import software.amazon.smithy.rust.codegen.server.python.smithy.pythonType import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstring +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * To share structures defined in Rust with Python, `pyo3` provides the `PyClass` trait. @@ -34,11 +37,13 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstrin */ class PythonServerStructureGenerator( model: Model, - private val symbolProvider: RustSymbolProvider, + private val codegenContext: ServerCodegenContext, private val writer: RustWriter, private val shape: StructureShape, -) : StructureGenerator(model, symbolProvider, writer, shape, emptyList()) { +) : StructureGenerator(model, codegenContext.symbolProvider, writer, shape, emptyList()) { + private val symbolProvider = codegenContext.symbolProvider + private val libName = codegenContext.settings.moduleName.toSnakeCase() private val pyO3 = PythonServerCargoDependency.PyO3.toType() override fun renderStructure() { @@ -58,6 +63,9 @@ class PythonServerStructureGenerator( writer.rustTemplate("#{ConstructorSignature:W}", "ConstructorSignature" to renderConstructorSignature()) super.renderStructure() renderPyO3Methods() + if (!shape.hasTrait()) { + renderPyBoxTraits() + } } override fun renderStructureMember( @@ -69,7 +77,7 @@ class PythonServerStructureGenerator( writer.addDependency(PythonServerCargoDependency.PyO3) // Above, we manually add dependency since we can't use a `RuntimeType` below Attribute("pyo3(get, set)").render(writer) - writer.rustTemplate("#{Signature:W}", "Signature" to renderSymbolSignature(memberSymbol)) + writer.rustTemplate("#{Signature:W}", "Signature" to renderMemberSignature(member, memberSymbol)) super.renderStructureMember(writer, member, memberName, memberSymbol) } @@ -99,6 +107,25 @@ class PythonServerStructureGenerator( ) } + private fun renderPyBoxTraits() { + writer.rustTemplate( + """ + impl<'source> #{pyo3}::FromPyObject<'source> for std::boxed::Box<$name> { + fn extract(ob: &'source #{pyo3}::PyAny) -> #{pyo3}::PyResult { + ob.extract::<$name>().map(Box::new) + } + } + + impl #{pyo3}::IntoPy<#{pyo3}::PyObject> for std::boxed::Box<$name> { + fn into_py(self, py: #{pyo3}::Python<'_>) -> #{pyo3}::PyObject { + (*self).into_py(py) + } + } + """, + "pyo3" to pyO3, + ) + } + private fun renderStructSignatureMembers(): Writable = writable { forEachMember(members) { _, memberName, memberSymbol -> @@ -116,17 +143,26 @@ class PythonServerStructureGenerator( private fun renderConstructorSignature(): Writable = writable { - forEachMember(members) { _, memberName, memberSymbol -> - val memberType = memberSymbol.rustType().pythonType() + forEachMember(members) { member, memberName, memberSymbol -> + val memberType = memberPythonType(member, memberSymbol) rust("/// :param $memberName ${memberType.renderAsDocstring()}:") } rust("/// :rtype ${PythonType.None.renderAsDocstring()}:") } - private fun renderSymbolSignature(symbol: Symbol): Writable = + private fun renderMemberSignature(shape: MemberShape, symbol: Symbol): Writable = writable { - val pythonType = symbol.rustType().pythonType() + val pythonType = memberPythonType(shape, symbol) rust("/// :type ${pythonType.renderAsDocstring()}:") } + + private fun memberPythonType(shape: MemberShape, symbol: Symbol): PythonType = + if (shape.isEventStream(model)) { + val eventStreamSymbol = PythonEventStreamSymbolProvider.parseSymbol(symbol) + val innerT = eventStreamSymbol.innerT.pythonType(libName) + PythonType.AsyncIterator(innerT) + } else { + symbol.rustType().pythonType(libName) + } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt index df083751ae..01a2a833af 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt @@ -26,6 +26,7 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.python.smithy.pythonType import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstring +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /* * Generate unions that are compatible with Python by wrapping the Rust implementation into @@ -34,11 +35,13 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstrin */ class PythonServerUnionGenerator( model: Model, - private val symbolProvider: SymbolProvider, + private val codegenContext: ServerCodegenContext, private val writer: RustWriter, shape: UnionShape, private val renderUnknownVariant: Boolean = true, -) : UnionGenerator(model, symbolProvider, writer, shape, renderUnknownVariant) { +) : UnionGenerator(model, codegenContext.symbolProvider, writer, shape, renderUnknownVariant) { + private val symbolProvider = codegenContext.symbolProvider + private val libName = codegenContext.settings.moduleName.toSnakeCase() private val sortedMembers: List = shape.allMembers.values.sortedBy { symbolProvider.toMemberName(it) } private val unionSymbol = symbolProvider.toSymbol(shape) @@ -121,11 +124,11 @@ class PythonServerUnionGenerator( ) writer.rust("/// :rtype ${unionSymbol.name}:") writer.rustBlock("pub fn $funcNamePart() -> Self") { - rust("Self(${unionSymbol.name}::$variantName") + rust("Self(${unionSymbol.name}::$variantName)") } } else { val memberSymbol = symbolProvider.toSymbol(member) - val pythonType = memberSymbol.rustType().pythonType() + val pythonType = memberSymbol.rustType().pythonType(libName) val targetType = memberSymbol.rustType() Attribute("staticmethod").render(writer) writer.rust( @@ -157,7 +160,7 @@ class PythonServerUnionGenerator( writer.rustBlockTemplate("pub fn as_$funcNamePart(&self) -> #{pyo3}::PyResult<()>", "pyo3" to pyo3) { rustTemplate( """ - self.0.as_$funcNamePart().map_err(#{pyo3}::exceptions::PyValueError::new_err( + self.0.as_$funcNamePart().map_err(|_| #{pyo3}::exceptions::PyValueError::new_err( "${unionSymbol.name} variant is not None" )) """, @@ -166,7 +169,7 @@ class PythonServerUnionGenerator( } } else { val memberSymbol = symbolProvider.toSymbol(member) - val pythonType = memberSymbol.rustType().pythonType() + val pythonType = memberSymbol.rustType().pythonType(libName) val targetSymbol = symbolProvider.toSymbol(model.expectShape(member.target)) val rustType = memberSymbol.rustType() writer.rust( @@ -181,12 +184,13 @@ class PythonServerUnionGenerator( } else { "variant.clone()" } + val errorVariant = memberSymbol.rustType().pythonType(libName).renderAsDocstring() rustTemplate( """ match self.0.as_$funcNamePart() { Ok(variant) => Ok($variantType), Err(_) => Err(#{pyo3}::exceptions::PyValueError::new_err( - r"${unionSymbol.name} variant is not of type ${memberSymbol.rustType().pythonType().renderAsDocstring()}" + r"${unionSymbol.name} variant is not of type $errorVariant" )), } """, diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/protocols/PythonServerProtocolLoader.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/protocols/PythonServerProtocolLoader.kt new file mode 100644 index 0000000000..f84d35e7cb --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/protocols/PythonServerProtocolLoader.kt @@ -0,0 +1,125 @@ + +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.protocols + +import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait +import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingSection +import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolLoader +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserCustomization +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserSection +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerAwsJsonFactory +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBoundProtocolCustomization +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBoundProtocolSection +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerRestJsonFactory + +/** + * Customization class used to force casting a non primitive type into one overriden by a new symbol provider, + * by explicitly calling `from()` on it. + * + * For example we use this in the server Python implementation, where we override types like [Blob], [DateTime] and [Document] + * with wrappers compatible with Python, without touching the original implementation coming from `aws-smithy-types`. + */ +class PythonServerAfterDeserializedMemberJsonParserCustomization(private val runtimeConfig: RuntimeConfig) : + JsonParserCustomization() { + override fun section(section: JsonParserSection): Writable = when (section) { + is JsonParserSection.AfterTimestampDeserializedMember -> writable { + rust(".map(#T::from)", PythonServerRuntimeType.dateTime(runtimeConfig).toSymbol()) + } + is JsonParserSection.AfterBlobDeserializedMember -> writable { + rust(".map(#T::from)", PythonServerRuntimeType.blob(runtimeConfig).toSymbol()) + } + is JsonParserSection.AfterDocumentDeserializedMember -> writable { + rust(".map(#T::from)", PythonServerRuntimeType.document(runtimeConfig).toSymbol()) + } + else -> emptySection + } +} + +/** + * Customization class used to force casting a non primitive type into one overriden by a new symbol provider, + * by explicitly calling `into()` on it. + */ +class PythonServerAfterDeserializedMemberServerHttpBoundCustomization() : + ServerHttpBoundProtocolCustomization() { + override fun section(section: ServerHttpBoundProtocolSection): Writable = when (section) { + is ServerHttpBoundProtocolSection.AfterTimestampDeserializedMember -> writable { + rust(".into()") + } + else -> emptySection + } +} + +/** + * Customization class used to force casting a `Vec` into one a Python `Vec` + */ +class PythonServerAfterDeserializedMemberHttpBindingCustomization(private val runtimeConfig: RuntimeConfig) : + HttpBindingCustomization() { + override fun section(section: HttpBindingSection): Writable = when (section) { + is HttpBindingSection.AfterDeserializingIntoADateTimeOfHttpHeaders -> writable { + rust(".into_iter().map(#T::from).collect()", PythonServerRuntimeType.dateTime(runtimeConfig).toSymbol()) + } + else -> emptySection + } +} + +class PythonServerProtocolLoader( + private val supportedProtocols: ProtocolMap, +) : ProtocolLoader(supportedProtocols) { + + companion object { + fun defaultProtocols(runtimeConfig: RuntimeConfig) = + mapOf( + RestJson1Trait.ID to ServerRestJsonFactory( + additionalParserCustomizations = listOf( + PythonServerAfterDeserializedMemberJsonParserCustomization(runtimeConfig), + ), + additionalServerHttpBoundProtocolCustomizations = listOf( + PythonServerAfterDeserializedMemberServerHttpBoundCustomization(), + ), + additionalHttpBindingCustomizations = listOf( + PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig), + ), + ), + AwsJson1_0Trait.ID to ServerAwsJsonFactory( + AwsJsonVersion.Json10, + additionalParserCustomizations = listOf( + PythonServerAfterDeserializedMemberJsonParserCustomization(runtimeConfig), + ), + additionalServerHttpBoundProtocolCustomizations = listOf( + PythonServerAfterDeserializedMemberServerHttpBoundCustomization(), + ), + additionalHttpBindingCustomizations = listOf( + PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig), + ), + ), + AwsJson1_1Trait.ID to ServerAwsJsonFactory( + AwsJsonVersion.Json11, + additionalParserCustomizations = listOf( + PythonServerAfterDeserializedMemberJsonParserCustomization(runtimeConfig), + ), + additionalServerHttpBoundProtocolCustomizations = listOf( + PythonServerAfterDeserializedMemberServerHttpBoundCustomization(), + ), + additionalHttpBindingCustomizations = listOf( + PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig), + ), + ), + ) + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt index 669279c8d5..c7e6023f3c 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt @@ -41,7 +41,7 @@ fun executePythonServerCodegenVisitor(pluginCtx: PluginContext) { fun cargoTest(workdir: Path) = // `--no-default-features` is required to disable `pyo3/extension-module` which causes linking errors // see `PyO3ExtensionModuleDecorator`'s comments fore more detail. - "cargo test --no-default-features".runCommand( + "cargo test --no-default-features --no-fail-fast".runCommand( workdir, mapOf( // Those are required to run tests on macOS, see: https://pyo3.rs/main/building_and_distribution#macos diff --git a/codegen-server/python/src/main/resources/stubgen.py b/codegen-server/python/src/main/resources/stubgen.py new file mode 120000 index 0000000000..6cc4240437 --- /dev/null +++ b/codegen-server/python/src/main/resources/stubgen.py @@ -0,0 +1 @@ +../../../../../rust-runtime/aws-smithy-http-server-python/stubgen.py \ No newline at end of file diff --git a/codegen-server/python/src/main/resources/stubgen.sh b/codegen-server/python/src/main/resources/stubgen.sh new file mode 120000 index 0000000000..5aee6f429d --- /dev/null +++ b/codegen-server/python/src/main/resources/stubgen.sh @@ -0,0 +1 @@ +../../../../../rust-runtime/aws-smithy-http-server-python/stubgen.sh \ No newline at end of file diff --git a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerSymbolProviderTest.kt b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerSymbolProviderTest.kt index c7467b58ed..e3ab955a9f 100644 --- a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerSymbolProviderTest.kt +++ b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerSymbolProviderTest.kt @@ -16,8 +16,8 @@ import software.amazon.smithy.rust.codegen.server.smithy.testutil.ServerTestRust import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestRustSettings internal class PythonServerSymbolProviderTest { - private val pythonBlobType = RustType.Opaque("Blob", "aws_smithy_http_server_python::types") - private val pythonTimestampType = RustType.Opaque("DateTime", "aws_smithy_http_server_python::types") + private val pythonBlobType = RustType.Opaque("Blob", "::aws_smithy_http_server_python::types") + private val pythonTimestampType = RustType.Opaque("DateTime", "::aws_smithy_http_server_python::types") @Test fun `python symbol provider rewrites timestamp shape symbol`() { diff --git a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt index 1473edcffe..c552d32edd 100644 --- a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt +++ b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt @@ -28,9 +28,8 @@ internal class PythonTypeInformationGenerationTest { val foo = model.lookup("test#Foo") val codegenContext = serverTestCodegenContext(model) - val symbolProvider = codegenContext.symbolProvider val writer = RustWriter.forModule("model") - PythonServerStructureGenerator(model, symbolProvider, writer, foo).render() + PythonServerStructureGenerator(model, codegenContext, writer, foo).render() val result = writer.toString() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolMetadataProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolMetadataProvider.kt index 0c83795f2f..2c7b4f1fc5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolMetadataProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolMetadataProvider.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolMetadataProvider import software.amazon.smithy.rust.codegen.core.smithy.containerDefaultMetadata @@ -47,6 +48,11 @@ class ConstrainedShapeSymbolMetadataProvider( derives += containerDefaultMetadata(shape, model).derives } + // We should _always_ be able to `#[derive(Debug)]`: all constrained shapes' types are simply tuple newtypes + // wrapping a single type which always implements `Debug`. + // The wrapped type may not _derive_ `Debug` though, hence why this line is required; see https://github.com/awslabs/smithy-rs/issues/2582. + derives += RuntimeType.Debug + val visibility = Visibility.publicIf(constrainedTypes, Visibility.PUBCRATE) return RustMetadata(derives, additionalAttributes, visibility) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidator.kt new file mode 100644 index 0000000000..7b54ff48b0 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidator.kt @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.traits.PatternTrait +import software.amazon.smithy.model.validation.AbstractValidator +import software.amazon.smithy.model.validation.ValidationEvent +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.expectTrait + +class PatternTraitEscapedSpecialCharsValidator : AbstractValidator() { + private val specialCharsWithEscapes = mapOf( + '\b' to "\\b", + '\u000C' to "\\f", + '\n' to "\\n", + '\r' to "\\r", + '\t' to "\\t", + ) + private val specialChars = specialCharsWithEscapes.keys + + override fun validate(model: Model): List { + val shapes = model.getStringShapesWithTrait(PatternTrait::class.java) + + model.getMemberShapesWithTrait(PatternTrait::class.java) + return shapes + .filter { shape -> checkMisuse(shape) } + .map { shape -> makeError(shape) } + .toList() + } + + private fun makeError(shape: Shape): ValidationEvent { + val pattern = shape.expectTrait() + val replacement = pattern.pattern.toString() + .map { specialCharsWithEscapes.getOrElse(it) { it.toString() } } + .joinToString("") + .dq() + val message = + """ + Non-escaped special characters used inside `@pattern`. + You must escape them: `@pattern($replacement)`. + See https://github.com/awslabs/smithy-rs/issues/2508 for more details. + """.trimIndent() + return error(shape, pattern, message) + } + + private fun checkMisuse(shape: Shape): Boolean { + val pattern = shape.expectTrait().pattern.pattern() + return pattern.any(specialChars::contains) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt index 32abe41d69..96b3b5739c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt @@ -335,7 +335,9 @@ class InnerModule(private val moduleDocProvider: ModuleDocProvider, debugMode: B inlineWriter } else { check(inlineModuleAndWriter.inlineModule == lookForModule) { - "The two inline modules have the same name but different attributes on them." + """The two inline modules have the same name but different attributes on them: + 1) ${inlineModuleAndWriter.inlineModule} + 2) $lookForModule""" } inlineModuleAndWriter.writer diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCargoDependency.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCargoDependency.kt index 1726057a9f..956a88ecf2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCargoDependency.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCargoDependency.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig object ServerCargoDependency { val AsyncTrait: CargoDependency = CargoDependency("async-trait", CratesIo("0.1")) val FormUrlEncoded: CargoDependency = CargoDependency("form_urlencoded", CratesIo("1")) + val FuturesUtil: CargoDependency = CargoDependency("futures-util", CratesIo("0.3")) val Mime: CargoDependency = CargoDependency("mime", CratesIo("0.3")) val Nom: CargoDependency = CargoDependency("nom", CratesIo("7")) val OnceCell: CargoDependency = CargoDependency("once_cell", CratesIo("1.13")) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index e6ef816331..156cc455eb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -45,7 +45,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGenerat import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveShapeBoxer -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasEventStreamMember import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -63,6 +63,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedT import software.amazon.smithy.rust.codegen.server.smithy.generators.MapConstraintViolationGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ScopeMacroGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGeneratorWithoutPublicConstrainedTypes import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator @@ -128,7 +129,7 @@ open class ServerCodegenVisitor( .protocolFor(context.model, service) this.protocolGeneratorFactory = protocolGeneratorFactory - model = codegenDecorator.transformModel(service, baseModel) + model = codegenDecorator.transformModel(service, baseModel, settings) val serverSymbolProviders = ServerSymbolProviders.from( settings, @@ -265,7 +266,7 @@ open class ServerCodegenVisitor( fileManifest.baseDir, timeout = settings.codegenConfig.formatTimeoutSeconds.toLong(), ) - } catch (err: CommandFailed) { + } catch (err: CommandError) { logger.info( "[rust-server-codegen] Failed to run cargo fmt: [${service.id}]\n${err.output}", ) @@ -610,6 +611,8 @@ open class ServerCodegenVisitor( codegenContext, serverProtocol, ).render(this) + + ScopeMacroGenerator(codegenContext).render(this) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt index 2f4ac7425b..46b18face5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt @@ -18,7 +18,7 @@ object ServerRuntimeType { ServerCargoDependency.smithyHttpServer(runtimeConfig).toType().resolve("routing::Router") fun protocol(name: String, path: String, runtimeConfig: RuntimeConfig) = - ServerCargoDependency.smithyHttpServer(runtimeConfig).toType().resolve("proto::$path::$name") + ServerCargoDependency.smithyHttpServer(runtimeConfig).toType().resolve("protocol::$path::$name") fun protocol(runtimeConfig: RuntimeConfig) = protocol("Protocol", "", runtimeConfig) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.kt index 15fde9837f..2c2ef75f74 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.kt @@ -27,7 +27,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.generators.DocHandlerGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.handlerImports @@ -73,49 +72,22 @@ class ServerModuleDocProvider(private val codegenContext: ServerCodegenContext) val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet(compareBy { it.id }) val firstOperation = operations.first() ?: return@writable - val firstOperationSymbol = codegenContext.symbolProvider.toSymbol(firstOperation) - val firstOperationName = firstOperationSymbol.name.toPascalCase() val crateName = codegenContext.settings.moduleName.toSnakeCase() rustTemplate( """ /// A collection of types representing each operation defined in the service closure. /// - /// ## Constructing an [`Operation`](#{SmithyHttpServer}::operation::OperationShapeExt) - /// - /// To apply middleware to specific operations the [`Operation`](#{SmithyHttpServer}::operation::Operation) - /// API must be used. - /// - /// Using the [`OperationShapeExt`](#{SmithyHttpServer}::operation::OperationShapeExt) trait - /// implemented on each ZST we can construct an [`Operation`](#{SmithyHttpServer}::operation::Operation) - /// with appropriate constraints given by Smithy. - /// - /// #### Example - /// - /// ```no_run - /// use $crateName::operation_shape::$firstOperationName; - /// use #{SmithyHttpServer}::operation::OperationShapeExt; - /// - #{HandlerImports:W} - /// - #{Handler:W} - /// - /// let operation = $firstOperationName::from_handler(handler) - /// .layer(todo!("Provide a layer implementation")); - /// ``` - /// - /// ## Use as Marker Structs - /// - /// The [plugin system](#{SmithyHttpServer}::plugin) also makes use of these + /// The [plugin system](#{SmithyHttpServer}::plugin) makes use of these /// [zero-sized types](https://doc.rust-lang.org/nomicon/exotic-sizes.html##zero-sized-types-zsts) (ZSTs) to - /// parameterize [`Plugin`](#{SmithyHttpServer}::plugin::Plugin) implementations. The traits, such as - /// [`OperationShape`](#{SmithyHttpServer}::operation::OperationShape) can be used to provide + /// parameterize [`Plugin`](#{SmithyHttpServer}::plugin::Plugin) implementations. Their traits, such as + /// [`OperationShape`](#{SmithyHttpServer}::operation::OperationShape), can be used to provide /// operation specific information to the [`Layer`](#{Tower}::Layer) being applied. """.trimIndent(), "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(codegenContext.runtimeConfig).toType(), "Tower" to ServerCargoDependency.Tower.toType(), - "Handler" to DocHandlerGenerator(codegenContext, firstOperation, "handler", commentToken = "///")::render, + "Handler" to DocHandlerGenerator(codegenContext, firstOperation, "handler", commentToken = "///").docSignature(), "HandlerImports" to handlerImports(crateName, operations, commentToken = "///"), ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt index 26f298c191..4dfa97644e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt @@ -22,7 +22,6 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.ShortShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.RangeTrait -import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.Trait import software.amazon.smithy.model.traits.UniqueItemsTrait @@ -62,10 +61,11 @@ private sealed class UnsupportedConstraintMessageKind { shape: Shape, constraintTrait: Trait, trackingIssue: String, + willSupport: Boolean = true, ) = buildMessage( "The ${shape.type} shape `${shape.id}` has the constraint trait `${constraintTrait.toShapeId()}` attached.", - willSupport = true, + willSupport, trackingIssue, ) @@ -105,7 +105,12 @@ private sealed class UnsupportedConstraintMessageKind { is UnsupportedRangeTraitOnShape -> LogMessage( level, - buildMessageShapeHasUnsupportedConstraintTrait(shape, rangeTrait, constraintTraitsUberIssue), + buildMessageShapeHasUnsupportedConstraintTrait( + shape, + rangeTrait, + willSupport = false, + trackingIssue = "https://github.com/awslabs/smithy-rs/issues/2007", + ), ) is UnsupportedUniqueItemsTraitOnShape -> LogMessage( @@ -158,8 +163,6 @@ data class LogMessage(val level: Level, val message: String) data class ValidationResult(val shouldAbort: Boolean, val messages: List) : Throwable(message = messages.joinToString("\n") { it.message }) -private val unsupportedConstraintsOnMemberShapes = allConstraintTraits - RequiredTrait::class.java - /** * Validate that all constrained operations have the shape [validationExceptionShapeId] shape attached to their errors. */ @@ -253,7 +256,7 @@ fun validateUnsupportedConstraints( unsupportedConstraintOnNonErrorShapeReachableViaAnEventStreamSet + unsupportedConstraintErrorShapeReachableViaAnEventStreamSet // 3. Range trait used on unsupported shapes. - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) + // TODO(https://github.com/awslabs/smithy-rs/issues/2007) val unsupportedRangeTraitOnShapeSet = walker .walkShapes(service) .asSequence() @@ -280,16 +283,28 @@ fun validateUnsupportedConstraints( .toSet() val messages = - unsupportedLengthTraitOnStreamingBlobShapeSet.map { - it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) - } + - unsupportedConstraintShapeReachableViaAnEventStreamSet.map { + ( + unsupportedLengthTraitOnStreamingBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - mapShapeReachableFromUniqueItemsListShapeSet.map { - it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) - } + unsupportedConstraintShapeReachableViaAnEventStreamSet.map { + it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) + } + + unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + mapShapeReachableFromUniqueItemsListShapeSet.map { + it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) + } + ).toMutableList() + + if (messages.isEmpty() && codegenConfig.ignoreUnsupportedConstraints) { + messages += LogMessage( + Level.SEVERE, + """ + The `ignoreUnsupportedConstraints` flag in the `codegen` configuration is set to `true`, but it has no + effect. All the constraint traits used in the model are well-supported, please remove this flag. + """.trimIndent().replace("\n", " "), + ) + } return ValidationResult(shouldAbort = messages.any { it.level == Level.SEVERE }, messages) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt index 032de89304..8842686d11 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.smithy.transformers.allErrors +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator /** @@ -34,7 +35,7 @@ class AddInternalServerErrorToInfallibleOperationsDecorator : ServerCodegenDecor override val name: String = "AddInternalServerErrorToInfallibleOperations" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ServerRustSettings): Model = addErrorShapeToModelOperations(service, model) { shape -> shape.allErrors(model).isEmpty() } } @@ -60,7 +61,7 @@ class AddInternalServerErrorToAllOperationsDecorator : ServerCodegenDecorator { override val name: String = "AddInternalServerErrorToAllOperations" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ServerRustSettings): Model = addErrorShapeToModelOperations(service, model) { true } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt index 8e771cb122..2522024efc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.CombinedCoreCod import software.amazon.smithy.rust.codegen.core.smithy.customize.CoreCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.server.smithy.ValidationResult import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator @@ -21,7 +22,7 @@ typealias ServerProtocolMap = ProtocolMap { +interface ServerCodegenDecorator : CoreCodegenDecorator { fun protocols(serviceId: ShapeId, currentProtocols: ServerProtocolMap): ServerProtocolMap = currentProtocols fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator? = null @@ -38,7 +39,7 @@ interface ServerCodegenDecorator : CoreCodegenDecorator { * This makes the actual concrete codegen simpler by not needing to deal with multiple separate decorators. */ class CombinedServerCodegenDecorator(private val decorators: List) : - CombinedCoreCodegenDecorator(decorators), + CombinedCoreCodegenDecorator(decorators), ServerCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/CollectionConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/CollectionConstraintViolationGenerator.kt index e2a177f536..b04eaa2f93 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/CollectionConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/CollectionConstraintViolationGenerator.kt @@ -48,7 +48,7 @@ class CollectionConstraintViolationGenerator( inlineModuleCreator(constraintViolationSymbol) { val constraintViolationVariants = constraintsInfo.map { it.constraintViolationVariant }.toMutableList() - if (isMemberConstrained) { + if (shape.isReachableFromOperationInput() && isMemberConstrained) { constraintViolationVariants += { val memberConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(targetShape).letIf( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGenerator.kt index 9b5775478e..f705e4af3a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGenerator.kt @@ -72,7 +72,7 @@ class ConstrainedCollectionGenerator( } val name = constrainedShapeSymbolProvider.toSymbol(shape).name - val inner = "std::vec::Vec<#{ValueMemberSymbol}>" + val inner = "::std::vec::Vec<#{ValueMemberSymbol}>" val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) val constrainedSymbol = symbolProvider.toSymbol(shape) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index 44992f4a48..8e52d0d4da 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -30,7 +31,7 @@ class ConstrainedTraitForEnumGenerator( val symbol = symbolProvider.toSymbol(shape) val name = symbol.name - val unconstrainedType = "String" + val unconstrainedType = RuntimeType.String.fullyQualifiedName() writer.rustTemplate( """ @@ -38,12 +39,13 @@ class ConstrainedTraitForEnumGenerator( type Unconstrained = $unconstrainedType; } - impl From<$unconstrainedType> for #{MaybeConstrained} { + impl #{From}<$unconstrainedType> for #{MaybeConstrained} { fn from(value: $unconstrainedType) -> Self { Self::Unconstrained(value) } } """, + *preludeScope, "ConstrainedTrait" to RuntimeType.ConstrainedTrait, "MaybeConstrained" to symbol.makeMaybeConstrained(), ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt index a0dcf07ba9..d67b79b455 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt @@ -6,10 +6,8 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -55,14 +53,25 @@ class DocHandlerGenerator( } } - fun render(writer: RustWriter) { - // This assumes that the `error` (if applicable) `input`, and `output` modules have been imported by the - // caller and hence are in scope. - writer.rustTemplate( - """ - #{Handler:W} - """, - "Handler" to docSignature(), - ) + /** + * Similarly to `docSignature`, returns the function signature of an operation handler implementation, with the + * difference that we don't ellide the error for use in `tower::service_fn`. + */ + fun docFixedSignature(): Writable { + val errorT = if (operation.errors.isEmpty()) { + "std::convert::Infallible" + } else { + "${ErrorModule.name}::${errorSymbol.name}" + } + + return writable { + rust( + """ + $commentToken async fn $handlerName(input: ${InputModule.name}::${inputSymbol.name}) -> Result<${OutputModule.name}::${outputSymbol.name}, $errorT> { + $commentToken todo!() + $commentToken } + """.trimIndent(), + ) + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 065a4067c7..4c1025a0d2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -45,10 +45,12 @@ class MapConstraintViolationGenerator( val constraintViolationName = constraintViolationSymbol.name val constraintViolationCodegenScopeMutableList: MutableList> = mutableListOf() - if (isKeyConstrained(keyShape, symbolProvider)) { + val keyConstraintViolationExists = shape.isReachableFromOperationInput() && isKeyConstrained(keyShape, symbolProvider) + val valueConstraintViolationExists = shape.isReachableFromOperationInput() && isValueConstrained(valueShape, model, symbolProvider) + if (keyConstraintViolationExists) { constraintViolationCodegenScopeMutableList.add("KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape)) } - if (isValueConstrained(valueShape, model, symbolProvider)) { + if (valueConstraintViolationExists) { constraintViolationCodegenScopeMutableList.add( "ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape).letIf( @@ -78,8 +80,8 @@ class MapConstraintViolationGenerator( ##[derive(Debug, PartialEq)] pub${ if (constraintViolationVisibility == Visibility.PUBCRATE) " (crate) " else "" } enum $constraintViolationName { ${if (shape.hasTrait()) "Length(usize)," else ""} - ${if (isKeyConstrained(keyShape, symbolProvider)) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} - ${if (isValueConstrained(valueShape, model, symbolProvider)) "##[doc(hidden)] Value(#{KeySymbol}, #{ValueConstraintViolationSymbol})," else ""} + ${if (keyConstraintViolationExists) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} + ${if (valueConstraintViolationExists) "##[doc(hidden)] Value(#{KeySymbol}, #{ValueConstraintViolationSymbol})," else ""} } """, *constraintViolationCodegenScope, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt new file mode 100644 index 0000000000..bb31a55d26 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt @@ -0,0 +1,182 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.knowledge.TopDownIndex +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.toPascalCase +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext + +class ScopeMacroGenerator( + private val codegenContext: ServerCodegenContext, +) { + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope = + arrayOf( + "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(runtimeConfig).toType(), + ) + + /** Calculate all `operationShape`s contained within the `ServiceShape`. */ + private val index = TopDownIndex.of(codegenContext.model) + private val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet(compareBy { it.id }) + + private fun macro(): Writable = writable { + val firstOperationName = codegenContext.symbolProvider.toSymbol(operations.first()).name.toPascalCase() + val operationNames = operations.joinToString(" ") { + codegenContext.symbolProvider.toSymbol(it).name.toPascalCase() + } + + // When writing `macro_rules!` we add whitespace between `$` and the arguments to avoid Kotlin templating. + + // To acheive the desired API we need to calculate the set theoretic complement `B \ A`. + // The macro below, for rules prefixed with `@`, encodes a state machine which performs this. + // The initial state is `(A) () (B)`, where `A` and `B` are lists of elements of `A` and `B`. + // The rules, in order: + // - Terminate on pattern `() (t0, t1, ...) (b0, b1, ...)`, the complement has been calculated as + // `{ t0, t1, ..., b0, b1, ...}`. + // - Send pattern `(x, a0, a1, ...) (t0, t1, ...) (x, b0, b1, ...)` to + // `(a0, a1, ...) (t0, t1, ...) (b0, b1, ...)`, eliminating a matching `x` from `A` and `B`. + // - Send pattern `(a0, a1, ...) (t0, t1, ...) ()` to `(a0, a1, ...) () (t0, t1, ...)`, restarting the search. + // - Send pattern `(a0, a1, ...) (t0, t1, ...) (b0, b1, ...)` to `(a0, a1, ...) (b0, t0, t1, ...) (b1, ...)`, + // iterating through the `B`. + val operationBranches = operations + .map { codegenContext.symbolProvider.toSymbol(it).name.toPascalCase() }.joinToString("") { + """ + // $it match found, pop from both `member` and `not_member` + (@ $ name: ident, $ contains: ident ($it $($ member: ident)*) ($($ temp: ident)*) ($it $($ not_member: ident)*)) => { + scope! { @ $ name, $ contains ($($ member)*) ($($ temp)*) ($($ not_member)*) } + }; + // $it match not found, pop from `not_member` into `temp` stack + (@ $ name: ident, $ contains: ident ($it $($ member: ident)*) ($($ temp: ident)*) ($ other: ident $($ not_member: ident)*)) => { + scope! { @ $ name, $ contains ($it $($ member)*) ($ other $($ temp)*) ($($ not_member)*) } + }; + """ + } + val crateName = codegenContext.moduleName.toSnakeCase() + + // If we have a second operation we can perform further checks + val otherOperationName: String? = operations.toList().getOrNull(1)?.let { + codegenContext.symbolProvider.toSymbol(it).name + } + val furtherTests = if (otherOperationName != null) { + writable { + rustTemplate( + """ + /// ## let a = Plugin::<(), $otherOperationName, u64>::apply(&scoped_a, 6); + /// ## let b = Plugin::<(), $otherOperationName, u64>::apply(&scoped_b, 6); + /// ## assert_eq!(a, 6_u64); + /// ## assert_eq!(b, 3_u32); + """, + ) + } + } else { + writable {} + } + + rustTemplate( + """ + /// A macro to help with scoping [plugins](#{SmithyHttpServer}::plugin) to a subset of all operations. + /// + /// In contrast to [`aws_smithy_http_server::scope`](#{SmithyHttpServer}::scope), this macro has knowledge + /// of the service and any operations _not_ specified will be placed in the opposing group. + /// + /// ## Example + /// + /// ```rust + /// scope! { + /// /// Includes [`$firstOperationName`], excluding all other operations. + /// struct ScopeA { + /// includes: [$firstOperationName] + /// } + /// } + /// + /// scope! { + /// /// Excludes [`$firstOperationName`], excluding all other operations. + /// struct ScopeB { + /// excludes: [$firstOperationName] + /// } + /// } + /// + /// ## use #{SmithyHttpServer}::plugin::{Plugin, Scoped}; + /// ## use $crateName::scope; + /// ## struct MockPlugin; + /// ## impl Plugin for MockPlugin { type Output = u32; fn apply(&self, input: T) -> u32 { 3 } } + /// ## let scoped_a = Scoped::new::(MockPlugin); + /// ## let scoped_b = Scoped::new::(MockPlugin); + /// ## let a = Plugin::<(), $crateName::operation_shape::$firstOperationName, u64>::apply(&scoped_a, 6); + /// ## let b = Plugin::<(), $crateName::operation_shape::$firstOperationName, u64>::apply(&scoped_b, 6); + /// ## assert_eq!(a, 3_u32); + /// ## assert_eq!(b, 6_u64); + /// ``` + ##[macro_export] + macro_rules! scope { + // Completed, render impls + (@ $ name: ident, $ contains: ident () ($($ temp: ident)*) ($($ not_member: ident)*)) => { + $( + impl #{SmithyHttpServer}::plugin::scoped::Membership<$ temp> for $ name { + type Contains = #{SmithyHttpServer}::plugin::scoped::$ contains; + } + )* + $( + impl #{SmithyHttpServer}::plugin::scoped::Membership<$ not_member> for $ name { + type Contains = #{SmithyHttpServer}::plugin::scoped::$ contains; + } + )* + }; + // All `not_member`s exhausted, move `temp` into `not_member` + (@ $ name: ident, $ contains: ident ($($ member: ident)*) ($($ temp: ident)*) ()) => { + scope! { @ $ name, $ contains ($($ member)*) () ($($ temp)*) } + }; + $operationBranches + ( + $(##[$ attrs:meta])* + $ vis:vis struct $ name:ident { + includes: [$($ include:ident),*] + } + ) => { + use $ crate::operation_shape::*; + #{SmithyHttpServer}::scope! { + $(##[$ attrs])* + $ vis struct $ name { + includes: [$($ include),*], + excludes: [] + } + } + scope! { @ $ name, False ($($ include)*) () ($operationNames) } + }; + ( + $(##[$ attrs:meta])* + $ vis:vis struct $ name:ident { + excludes: [$($ exclude:ident),*] + } + ) => { + use $ crate::operation_shape::*; + + #{SmithyHttpServer}::scope! { + $(##[$ attrs])* + $ vis struct $ name { + includes: [], + excludes: [$($ exclude),*] + } + } + scope! { @ $ name, True ($($ exclude)*) () ($operationNames) } + }; + } + """, + *codegenScope, + "FurtherTests" to furtherTests, + ) + } + + fun render(writer: RustWriter) { + macro()(writer) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index cc811b80f2..09a0d2d5cd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -8,9 +8,11 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumType @@ -65,7 +67,7 @@ open class ConstrainedEnum( } rustBlock("impl #T<&str> for ${context.enumName}", RuntimeType.TryFrom) { rust("type Error = #T;", constraintViolationSymbol) - rustBlock("fn try_from(s: &str) -> Result>::Error>", RuntimeType.TryFrom) { + rustBlockTemplate("fn try_from(s: &str) -> #{Result}>::Error>", *preludeScope) { rustBlock("match s") { context.sortedMembers.forEach { member -> rust("${member.value.dq()} => Ok(${context.enumName}::${member.derivedName()}),") @@ -78,13 +80,12 @@ open class ConstrainedEnum( """ impl #{TryFrom}<#{String}> for ${context.enumName} { type Error = #{ConstraintViolation}; - fn try_from(s: #{String}) -> std::result::Result>::Error> { + fn try_from(s: #{String}) -> #{Result}>::Error> { s.as_str().try_into() } } """, - "String" to RuntimeType.String, - "TryFrom" to RuntimeType.TryFrom, + *preludeScope, "ConstraintViolation" to constraintViolationSymbol, ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt index ee09c47047..0330b05905 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt @@ -43,7 +43,7 @@ open class ServerOperationErrorGenerator( (operationOrEventStream as UnionShape).eventStreamErrors() .map { model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) } - fun render(writer: RustWriter) { + open fun render(writer: RustWriter) { val (errorSymbol, errors) = when (operationOrEventStream) { is OperationShape -> symbolProvider.symbolForOperationError(operationOrEventStream) to operationErrors() is UnionShape -> symbolProvider.symbolForEventStreamError(operationOrEventStream) to eventStreamErrors() diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt index 451aa65a85..3a5af6cb50 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency @@ -49,12 +50,13 @@ class ServerOperationGenerator( val requestFmt = generator.requestFmt() val responseFmt = generator.responseFmt() + val operationIdAbsolute = operationId.toString().replace("#", "##") writer.rustTemplate( """ pub struct $operationName; impl #{SmithyHttpServer}::operation::OperationShape for $operationName { - const NAME: &'static str = "${operationId.toString().replace("#", "##")}"; + const ID: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new(${operationIdAbsolute.dq()}, ${operationId.namespace.dq()}, ${operationId.name.dq()}); type Input = crate::input::${operationName}Input; type Output = crate::output::${operationName}Output; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt index a8221b9327..e300d248b1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt @@ -53,7 +53,7 @@ open class ServerRootGenerator( val hasErrors = operations.any { it.errors.isNotEmpty() } val handlers: Writable = operations .map { operation -> - DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!")::render + DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!").docSignature() } .join("//!\n") @@ -106,20 +106,22 @@ open class ServerRootGenerator( //! #### Plugins //! //! The [`$serviceName::builder_with_plugins`] method, returning [`$builderName`], - //! accepts a [`Plugin`](aws_smithy_http_server::plugin::Plugin). + //! accepts a plugin marked with [`HttpMarker`](aws_smithy_http_server::plugin::HttpMarker) and a + //! plugin marked with [`ModelMarker`](aws_smithy_http_server::plugin::ModelMarker). //! Plugins allow you to build middleware which is aware of the operation it is being applied to. //! //! ```rust + //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin; //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as LoggingPlugin; //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as MetricsPlugin; //! ## use #{Hyper}::Body; - //! use #{SmithyHttpServer}::plugin::PluginPipeline; + //! use #{SmithyHttpServer}::plugin::HttpPlugins; //! use $crateName::{$serviceName, $builderName}; //! - //! let plugins = PluginPipeline::new() + //! let http_plugins = HttpPlugins::new() //! .push(LoggingPlugin) //! .push(MetricsPlugin); - //! let builder: $builderName = $serviceName::builder_with_plugins(plugins); + //! let builder: $builderName = $serviceName::builder_with_plugins(http_plugins, IdentityPlugin); //! ``` //! //! Check out [`#{SmithyHttpServer}::plugin`] to learn more about plugins. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt index b184ccbaa4..a6f62246d8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt @@ -9,14 +9,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency -import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType class ServerRuntimeTypesReExportsGenerator( codegenContext: CodegenContext, ) { private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = arrayOf( - "Router" to ServerRuntimeType.router(runtimeConfig), "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(runtimeConfig).toType(), ) @@ -28,11 +26,13 @@ class ServerRuntimeTypesReExportsGenerator( } pub mod operation { pub use #{SmithyHttpServer}::operation::OperationShape; - pub use #{SmithyHttpServer}::operation::Operation; } pub mod plugin { + pub use #{SmithyHttpServer}::plugin::HttpPlugins; + pub use #{SmithyHttpServer}::plugin::ModelPlugins; + pub use #{SmithyHttpServer}::plugin::HttpMarker; + pub use #{SmithyHttpServer}::plugin::ModelMarker; pub use #{SmithyHttpServer}::plugin::Plugin; - pub use #{SmithyHttpServer}::plugin::PluginPipeline; pub use #{SmithyHttpServer}::plugin::PluginStack; } pub mod request { @@ -51,7 +51,7 @@ class ServerRuntimeTypesReExportsGenerator( } pub use #{SmithyHttpServer}::instrumentation; - pub use #{SmithyHttpServer}::proto; + pub use #{SmithyHttpServer}::protocol; pub use #{SmithyHttpServer}::Extension; """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index 160f9fd685..2b4e2ee2e9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -50,9 +50,9 @@ class ServerServiceGenerator( private val crateName = codegenContext.moduleUseName() private val service = codegenContext.serviceShape - private val serviceName = service.id.name.toPascalCase() + private val serviceId = service.id + private val serviceName = serviceId.name.toPascalCase() private val builderName = "${serviceName}Builder" - private val builderPluginGenericTypeName = "Plugin" private val builderBodyGenericTypeName = "Body" /** Calculate all `operationShape`s contained within the `ServiceShape`. */ @@ -105,6 +105,9 @@ class ServerServiceGenerator( private fun builderSetters(): Writable = writable { for ((operationShape, structName) in operationStructNames) { val fieldName = builderFieldNames[operationShape] + val docHandler = DocHandlerGenerator(codegenContext, operationShape, "handler", "///") + val handler = docHandler.docSignature() + val handlerFixed = docHandler.docFixedSignature() rustTemplate( """ /// Sets the [`$structName`](crate::operation_shape::$structName) operation. @@ -129,44 +132,123 @@ class ServerServiceGenerator( /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; /// ``` /// - pub fn $fieldName(self, handler: HandlerType) -> Self + pub fn $fieldName(self, handler: HandlerType) -> Self where HandlerType: #{SmithyHttpServer}::operation::Handler, - #{SmithyHttpServer}::operation::Operation<#{SmithyHttpServer}::operation::IntoService>: - #{SmithyHttpServer}::operation::Upgradable< - #{Protocol}, - crate::operation_shape::$structName, - ServiceExtractors, - $builderBodyGenericTypeName, - $builderPluginGenericTypeName, - > + + ModelPl: #{SmithyHttpServer}::plugin::Plugin< + $serviceName, + crate::operation_shape::$structName, + #{SmithyHttpServer}::operation::IntoService + >, + #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< + $serviceName, + crate::operation_shape::$structName, + ModelPl::Output + >, + HttpPl: #{SmithyHttpServer}::plugin::Plugin< + $serviceName, + crate::operation_shape::$structName, + < + #{SmithyHttpServer}::operation::UpgradePlugin:: + as #{SmithyHttpServer}::plugin::Plugin< + $serviceName, + crate::operation_shape::$structName, + ModelPl::Output + > + >::Output + >, + + HttpPl::Output: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + >>::Future: Send + 'static, + { use #{SmithyHttpServer}::operation::OperationShapeExt; - self.${fieldName}_operation(crate::operation_shape::$structName::from_handler(handler)) + use #{SmithyHttpServer}::plugin::Plugin; + let svc = crate::operation_shape::$structName::from_handler(handler); + let svc = self.model_plugin.apply(svc); + let svc = #{SmithyHttpServer}::operation::UpgradePlugin::::new().apply(svc); + let svc = self.http_plugin.apply(svc); + self.${fieldName}_custom(svc) } /// Sets the [`$structName`](crate::operation_shape::$structName) operation. /// - /// This should be an [`Operation`](#{SmithyHttpServer}::operation::Operation) created from - /// [`$structName`](crate::operation_shape::$structName) using either - /// [`OperationShape::from_handler`](#{SmithyHttpServer}::operation::OperationShapeExt::from_handler) or - /// [`OperationShape::from_service`](#{SmithyHttpServer}::operation::OperationShapeExt::from_service). - pub fn ${fieldName}_operation(mut self, operation: Operation) -> Self + /// This should be an async function satisfying the [`Handler`](#{SmithyHttpServer}::operation::Handler) trait. + /// See the [operation module documentation](#{SmithyHttpServer}::operation) for more information. + /// + /// ## Example + /// + /// ```no_run + /// use $crateName::$serviceName; + /// + #{HandlerImports:W} + /// + #{HandlerFixed:W} + /// + /// let svc = #{Tower}::util::service_fn(handler); + /// let app = $serviceName::builder_without_plugins() + /// .${fieldName}_service(svc) + /// /* Set other handlers */ + /// .build() + /// .unwrap(); + /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; + /// ``` + /// + pub fn ${fieldName}_service(self, service: S) -> Self where - Operation: #{SmithyHttpServer}::operation::Upgradable< - #{Protocol}, + S: #{SmithyHttpServer}::operation::OperationService, + + ModelPl: #{SmithyHttpServer}::plugin::Plugin< + $serviceName, crate::operation_shape::$structName, - Extractors, - $builderBodyGenericTypeName, - $builderPluginGenericTypeName, - > + #{SmithyHttpServer}::operation::Normalize + >, + #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< + $serviceName, + crate::operation_shape::$structName, + ModelPl::Output + >, + HttpPl: #{SmithyHttpServer}::plugin::Plugin< + $serviceName, + crate::operation_shape::$structName, + < + #{SmithyHttpServer}::operation::UpgradePlugin:: + as #{SmithyHttpServer}::plugin::Plugin< + $serviceName, + crate::operation_shape::$structName, + ModelPl::Output + > + >::Output + >, + + HttpPl::Output: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + >>::Future: Send + 'static, + { - self.$fieldName = Some(operation.upgrade(&self.plugin)); + use #{SmithyHttpServer}::operation::OperationShapeExt; + use #{SmithyHttpServer}::plugin::Plugin; + let svc = crate::operation_shape::$structName::from_service(service); + let svc = self.model_plugin.apply(svc); + let svc = #{SmithyHttpServer}::operation::UpgradePlugin::::new().apply(svc); + let svc = self.http_plugin.apply(svc); + self.${fieldName}_custom(svc) + } + + /// Sets the [`$structName`](crate::operation_shape::$structName) to a custom [`Service`](tower::Service). + /// not constrained by the Smithy contract. + fn ${fieldName}_custom(mut self, svc: S) -> Self + where + S: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + S::Future: Send + 'static, + { + self.$fieldName = Some(#{SmithyHttpServer}::routing::Route::new(svc)); self } """, "Protocol" to protocol.markerStruct(), - "Handler" to DocHandlerGenerator(codegenContext, operationShape, "handler", "///")::render, + "Handler" to handler, + "HandlerFixed" to handlerFixed, "HandlerImports" to handlerImports(crateName, operations), *codegenScope, ) @@ -187,7 +269,7 @@ class ServerServiceGenerator( rust( """ if self.$fieldName.is_none() { - $missingOperationsVariableName.insert(crate::operation_shape::$operationZstTypeName::NAME, ".$fieldName()"); + $missingOperationsVariableName.insert(crate::operation_shape::$operationZstTypeName::ID, ".$fieldName()"); } """, ) @@ -272,19 +354,13 @@ class ServerServiceGenerator( for (operationShape in operations) { val fieldName = builderFieldNames[operationShape]!! val (specBuilderFunctionName, _) = requestSpecMap.getValue(operationShape) - val operationZstTypeName = operationStructNames[operationShape]!! rustTemplate( """ ( $requestSpecsModuleName::$specBuilderFunctionName(), self.$fieldName.unwrap_or_else(|| { - #{SmithyHttpServer}::routing::Route::new(<#{SmithyHttpServer}::operation::FailOnMissingOperation as #{SmithyHttpServer}::operation::Upgradable< - #{Protocol}, - crate::operation_shape::$operationZstTypeName, - (), - _, - _, - >>::upgrade(#{SmithyHttpServer}::operation::FailOnMissingOperation, &self.plugin)) + let svc = #{SmithyHttpServer}::operation::MissingFailure::<#{Protocol}>::default(); + #{SmithyHttpServer}::routing::Route::new(svc) }) ), """, @@ -318,7 +394,7 @@ class ServerServiceGenerator( /** Returns a `Writable` containing the builder struct definition and its implementations. */ private fun builder(): Writable = writable { - val builderGenerics = listOf(builderBodyGenericTypeName, builderPluginGenericTypeName).joinToString(", ") + val builderGenerics = listOf(builderBodyGenericTypeName, "HttpPl", "ModelPl").joinToString(", ") rustTemplate( """ /// The service builder for [`$serviceName`]. @@ -326,7 +402,8 @@ class ServerServiceGenerator( /// Constructed via [`$serviceName::builder_with_plugins`] or [`$serviceName::builder_without_plugins`]. pub struct $builderName<$builderGenerics> { ${builderFields.joinToString(", ")}, - plugin: $builderPluginGenericTypeName, + http_plugin: HttpPl, + model_plugin: ModelPl } impl<$builderGenerics> $builderName<$builderGenerics> { @@ -396,20 +473,22 @@ class ServerServiceGenerator( /// /// Use [`$serviceName::builder_without_plugins`] if you don't need to apply plugins. /// - /// Check out [`PluginPipeline`](#{SmithyHttpServer}::plugin::PluginPipeline) if you need to apply + /// Check out [`HttpPlugins`](#{SmithyHttpServer}::plugin::HttpPlugins) and + /// [`ModelPlugins`](#{SmithyHttpServer}::plugin::ModelPlugins) if you need to apply /// multiple plugins. - pub fn builder_with_plugins(plugin: Plugin) -> $builderName { + pub fn builder_with_plugins(http_plugin: HttpPl, model_plugin: ModelPl) -> $builderName { $builderName { #{NotSetFields:W}, - plugin + http_plugin, + model_plugin } } /// Constructs a builder for [`$serviceName`]. /// /// Use [`$serviceName::builder_with_plugins`] if you need to specify plugins. - pub fn builder_without_plugins() -> $builderName { - Self::builder_with_plugins(#{SmithyHttpServer}::plugin::IdentityPlugin) + pub fn builder_without_plugins() -> $builderName { + Self::builder_with_plugins(#{SmithyHttpServer}::plugin::IdentityPlugin, #{SmithyHttpServer}::plugin::IdentityPlugin) } } @@ -478,13 +557,13 @@ class ServerServiceGenerator( } private fun missingOperationsError(): Writable = writable { - rust( + rustTemplate( """ /// The error encountered when calling the [`$builderName::build`] method if one or more operation handlers are not /// specified. ##[derive(Debug)] pub struct MissingOperationsError { - operation_names2setter_methods: std::collections::HashMap<&'static str, &'static str>, + operation_names2setter_methods: std::collections::HashMap<#{SmithyHttpServer}::shape_id::ShapeId, &'static str>, } impl std::fmt::Display for MissingOperationsError { @@ -495,7 +574,7 @@ class ServerServiceGenerator( We are missing handlers for the following operations:\n", )?; for operation_name in self.operation_names2setter_methods.keys() { - writeln!(f, "- {}", operation_name)?; + writeln!(f, "- {}", operation_name.absolute())?; } writeln!(f, "\nUse the dedicated methods on `$builderName` to register the missing handlers:")?; @@ -508,9 +587,79 @@ class ServerServiceGenerator( impl std::error::Error for MissingOperationsError {} """, + *codegenScope, ) } + private fun serviceShapeImpl(): Writable = writable { + val namespace = serviceId.namespace + val name = serviceId.name + val absolute = serviceId.toString().replace("#", "##") + val version = codegenContext.serviceShape.version?.let { "Some(\"$it\")" } ?: "None" + rustTemplate( + """ + impl #{SmithyHttpServer}::service::ServiceShape for $serviceName { + const ID: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new("$absolute", "$namespace", "$name"); + + const VERSION: Option<&'static str> = $version; + + type Protocol = #{Protocol}; + + type Operations = Operation; + } + """, + "Protocol" to protocol.markerStruct(), + *codegenScope, + ) + } + + private fun operationEnum(): Writable = writable { + val operations = operationStructNames.values.joinToString(",") + val matchArms: Writable = operationStructNames.map { + (shape, name) -> + writable { + val absolute = shape.id.toString().replace("#", "##") + rustTemplate( + """ + Operation::$name => #{SmithyHttpServer}::shape_id::ShapeId::new("$absolute", "${shape.id.namespace}", "${shape.id.name}") + """, + *codegenScope, + ) + } + }.join(",") + rustTemplate( + """ + /// An enumeration of all [operations](https://smithy.io/2.0/spec/service-types.html##operation) in $serviceName. + ##[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub enum Operation { + $operations + } + + impl Operation { + /// Returns the [operations](https://smithy.io/2.0/spec/service-types.html##operation) [`ShapeId`](#{SmithyHttpServer}::shape_id::ShapeId). + pub fn shape_id(&self) -> #{SmithyHttpServer}::shape_id::ShapeId { + match self { + #{Arms} + } + } + } + """, + *codegenScope, + "Arms" to matchArms, + ) + + for ((_, value) in operationStructNames) { + rustTemplate( + """ + impl #{SmithyHttpServer}::service::ContainsOperation for $serviceName { + const VALUE: Operation = Operation::$value; + } + """, + *codegenScope, + ) + } + } + fun render(writer: RustWriter) { writer.rustTemplate( """ @@ -521,11 +670,17 @@ class ServerServiceGenerator( #{RequestSpecs:W} #{Struct:W} + + #{Operations} + + #{ServiceImpl} """, "Builder" to builder(), "MissingOperationsError" to missingOperationsError(), "RequestSpecs" to requestSpecsModule(), "Struct" to serviceStruct(), + "Operations" to operationEnum(), + "ServiceImpl" to serviceShapeImpl(), *codegenScope, ) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RestRequestSpecGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/RestRequestSpecGenerator.kt similarity index 89% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RestRequestSpecGenerator.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/RestRequestSpecGenerator.kt index 46890998a2..b5c0cd052b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RestRequestSpecGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/RestRequestSpecGenerator.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy.generators.http +package software.amazon.smithy.rust.codegen.server.smithy.generators.http import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -15,10 +15,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingReso /** * [RestRequestSpecGenerator] generates a restJson1 or restXml specific `RequestSpec`. Both protocols are routed the same. - * - * This class has to live in the `codegen-core` subproject instead of in the `codegen-server` subproject because it is used - * by the implementations of the `serverRouterRequestSpec` of the [Protocol] interface, which is used by both subprojects - * (even though only the `codegen-server` subproject calls `serverRouterRequestSpec`). */ class RestRequestSpecGenerator( private val httpBindingResolver: HttpBindingResolver, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index 41201b8695..e1e6c747f7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -28,6 +28,7 @@ class ServerRequestBindingGenerator( protocol: Protocol, codegenContext: ServerCodegenContext, operationShape: OperationShape, + additionalHttpBindingCustomizations: List = listOf(), ) { private val httpBindingGenerator = HttpBindingGenerator( @@ -39,7 +40,7 @@ class ServerRequestBindingGenerator( ServerRequestAfterDeserializingIntoAHashMapOfHttpPrefixHeadersWrapInUnconstrainedMapHttpBindingCustomization( codegenContext, ), - ), + ) + additionalHttpBindingCustomizations, ) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = @@ -81,5 +82,6 @@ class ServerRequestAfterDeserializingIntoAHashMapOfHttpPrefixHeadersWrapInUncons ) } } + else -> emptySection } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index cc47830054..01448d27a4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -71,6 +71,7 @@ class ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstr is HttpBindingSection.BeforeRenderingHeaderValue, is HttpBindingSection.AfterDeserializingIntoAHashMapOfHttpPrefixHeaders, + is HttpBindingSection.AfterDeserializingIntoADateTimeOfHttpHeaders, -> emptySection } } @@ -100,6 +101,7 @@ class ServerResponseBeforeRenderingHeadersHttpBindingCustomization(val codegenCo is HttpBindingSection.BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders, is HttpBindingSection.AfterDeserializingIntoAHashMapOfHttpPrefixHeaders, + is HttpBindingSection.AfterDeserializingIntoADateTimeOfHttpHeaders, -> emptySection } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index eb41a35e51..77ba711fd2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.protocol +import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape @@ -14,9 +15,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.http.RestRequestSpecGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion +import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml @@ -32,12 +33,13 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.generators.http.RestRequestSpecGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerAwsJsonSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerRestJsonSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape interface ServerProtocol : Protocol { - /** The path such that `aws_smithy_http_server::proto::$path` points to the protocol's module. */ + /** The path such that `aws_smithy_http_server::protocol::$path` points to the protocol's module. */ val protocolModulePath: String /** Returns the Rust marker struct enjoying `OperationShape`. */ @@ -77,22 +79,49 @@ interface ServerProtocol : Protocol { /** The protocol-specific `RequestRejection` type. **/ fun requestRejection(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::$protocolModulePath::rejection::RequestRejection") + .toType().resolve("protocol::$protocolModulePath::rejection::RequestRejection") /** The protocol-specific `ResponseRejection` type. **/ fun responseRejection(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::$protocolModulePath::rejection::ResponseRejection") + .toType().resolve("protocol::$protocolModulePath::rejection::ResponseRejection") /** The protocol-specific `RuntimeError` type. **/ fun runtimeError(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::$protocolModulePath::runtime_error::RuntimeError") + .toType().resolve("protocol::$protocolModulePath::runtime_error::RuntimeError") } +fun returnSymbolToParseFn(codegenContext: ServerCodegenContext): (Shape) -> ReturnSymbolToParse { + fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = + if (shape.canReachConstrainedShape(codegenContext.model, codegenContext.symbolProvider)) { + ReturnSymbolToParse(codegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) + } else { + ReturnSymbolToParse(codegenContext.symbolProvider.toSymbol(shape), false) + } + return ::returnSymbolToParse +} + +fun jsonParserGenerator( + codegenContext: ServerCodegenContext, + httpBindingResolver: HttpBindingResolver, + jsonName: (MemberShape) -> String, + additionalParserCustomizations: List = listOf(), +): JsonParserGenerator = + JsonParserGenerator( + codegenContext, + httpBindingResolver, + jsonName, + returnSymbolToParseFn(codegenContext), + listOf( + ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization(codegenContext), + ) + additionalParserCustomizations, + ) + class ServerAwsJsonProtocol( private val serverCodegenContext: ServerCodegenContext, awsJsonVersion: AwsJsonVersion, + private val additionalParserCustomizations: List = listOf(), ) : AwsJson(serverCodegenContext, awsJsonVersion), ServerProtocol { private val runtimeConfig = codegenContext.runtimeConfig @@ -102,25 +131,10 @@ class ServerAwsJsonProtocol( is AwsJsonVersion.Json11 -> "aws_json_11" } - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = - if (shape.canReachConstrainedShape(codegenContext.model, serverCodegenContext.symbolProvider)) { - ReturnSymbolToParse(serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) - } else { - ReturnSymbolToParse(codegenContext.symbolProvider.toSymbol(shape), false) - } - return JsonParserGenerator( - codegenContext, - httpBindingResolver, - ::awsJsonFieldName, - ::returnSymbolToParse, - listOf( - ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization(serverCodegenContext), - ), - ) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + jsonParserGenerator(serverCodegenContext, httpBindingResolver, ::awsJsonFieldName, additionalParserCustomizations) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = ServerAwsJsonSerializerGenerator(serverCodegenContext, httpBindingResolver, awsJsonVersion) override fun markerStruct(): RuntimeType { @@ -131,7 +145,7 @@ class ServerAwsJsonProtocol( } override fun routerType() = ServerCargoDependency.smithyHttpServer(runtimeConfig).toType() - .resolve("proto::aws_json::router::AwsJsonRouter") + .resolve("protocol::aws_json::router::AwsJsonRouter") /** * Returns the operation name as required by the awsJson1.x protocols. @@ -156,47 +170,31 @@ class ServerAwsJsonProtocol( override fun requestRejection(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::aws_json::rejection::RequestRejection") + .toType().resolve("protocol::aws_json::rejection::RequestRejection") override fun responseRejection(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::aws_json::rejection::ResponseRejection") + .toType().resolve("protocol::aws_json::rejection::ResponseRejection") override fun runtimeError(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::aws_json::runtime_error::RuntimeError") + .toType().resolve("protocol::aws_json::runtime_error::RuntimeError") } private fun restRouterType(runtimeConfig: RuntimeConfig) = ServerCargoDependency.smithyHttpServer(runtimeConfig).toType() - .resolve("proto::rest::router::RestRouter") + .resolve("protocol::rest::router::RestRouter") class ServerRestJsonProtocol( private val serverCodegenContext: ServerCodegenContext, + private val additionalParserCustomizations: List = listOf(), ) : RestJson(serverCodegenContext), ServerProtocol { val runtimeConfig = codegenContext.runtimeConfig override val protocolModulePath: String = "rest_json_1" - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = - if (shape.canReachConstrainedShape(codegenContext.model, codegenContext.symbolProvider)) { - ReturnSymbolToParse(serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) - } else { - ReturnSymbolToParse(serverCodegenContext.symbolProvider.toSymbol(shape), false) - } - return JsonParserGenerator( - codegenContext, - httpBindingResolver, - ::restJsonFieldName, - ::returnSymbolToParse, - listOf( - ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization( - serverCodegenContext, - ), - ), - ) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + jsonParserGenerator(serverCodegenContext, httpBindingResolver, ::restJsonFieldName, additionalParserCustomizations) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = ServerRestJsonSerializerGenerator(serverCodegenContext, httpBindingResolver) override fun markerStruct() = ServerRuntimeType.protocol("RestJson1", protocolModulePath, runtimeConfig) @@ -259,5 +257,6 @@ class ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonPa rust(".map(|x| x.into())") } } + else -> emptySection } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolGenerator.kt index 3605889297..419529f23a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolGenerator.kt @@ -7,15 +7,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBoundProtocolTraitImplGenerator open class ServerProtocolGenerator( - codegenContext: CodegenContext, val protocol: ServerProtocol, private val traitGenerator: ServerHttpBoundProtocolTraitImplGenerator, -) : ProtocolGenerator(codegenContext, protocol) { +) { /** * The server implementation uses this method to generate implementations of the `from_request` and `into_response` * traits for operation input and output shapes, respectively. @@ -24,6 +21,6 @@ open class ServerProtocolGenerator( operationWriter: RustWriter, operationShape: OperationShape, ) { - traitGenerator.generateTraitImpls(operationWriter, operationShape, emptyList()) + traitGenerator.generateTraitImpls(operationWriter, operationShape) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 34270f2fdf..ee0038e53c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -230,26 +230,6 @@ class ServerProtocolTestGenerator( // not been written with a server-side perspective in mind. private fun List.fixBroken(): List = this.map { when (it) { - is TestCase.RequestTest -> { - val howToFixIt = BrokenRequestTests[Pair(codegenContext.serviceShape.id.toString(), it.id)] - if (howToFixIt == null) { - it - } else { - val fixed = howToFixIt(it.testCase, it.operationShape) - TestCase.RequestTest(fixed, it.operationShape) - } - } - - is TestCase.ResponseTest -> { - val howToFixIt = BrokenResponseTests[Pair(codegenContext.serviceShape.id.toString(), it.id)] - if (howToFixIt == null) { - it - } else { - val fixed = howToFixIt(it.testCase) - TestCase.ResponseTest(fixed, it.targetShape) - } - } - is TestCase.MalformedRequestTest -> { val howToFixIt = BrokenMalformedRequestTests[Pair(codegenContext.serviceShape.id.toString(), it.id)] if (howToFixIt == null) { @@ -259,6 +239,7 @@ class ServerProtocolTestGenerator( TestCase.MalformedRequestTest(fixed) } } + else -> it } } @@ -810,8 +791,16 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsLongList", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsTimestampList", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsDateTimeList", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsHttpDateList_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsHttpDateList_case1", TestType.MalformedRequest), + FailingTest( + RestJsonValidation, + "RestJsonMalformedUniqueItemsHttpDateList_case0", + TestType.MalformedRequest, + ), + FailingTest( + RestJsonValidation, + "RestJsonMalformedUniqueItemsHttpDateList_case1", + TestType.MalformedRequest, + ), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsEnumList", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsIntEnumList", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsListList", TestType.MalformedRequest), @@ -830,12 +819,34 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedEnumString_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedEnumUnion_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedEnumUnion_case1", TestType.MalformedRequest), + + // TODO(https://github.com/awslabs/smithy/issues/1737): Specs on @internal, @tags, and enum values need to be clarified + FailingTest(RestJsonValidation, "RestJsonMalformedEnumTraitString_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumTraitString_case1", TestType.MalformedRequest), ) private val RunOnly: Set? = null // These tests are not even attempted to be generated, either because they will not compile // or because they are flaky - private val DisableTests = setOf() + private val DisableTests = setOf( + // TODO(https://github.com/awslabs/smithy-rs/issues/2891): Implement support for `@requestCompression` + "SDKAppendedGzipAfterProvidedEncoding_restJson1", + "SDKAppendedGzipAfterProvidedEncoding_restXml", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_0", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_1", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsQuery", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_ec2Query", + "SDKAppliedContentEncoding_awsJson1_0", + "SDKAppliedContentEncoding_awsJson1_1", + "SDKAppliedContentEncoding_awsQuery", + "SDKAppliedContentEncoding_ec2Query", + "SDKAppliedContentEncoding_restJson1", + "SDKAppliedContentEncoding_restXml", + + // RestXml S3 tests that fail to compile + "S3EscapeObjectKeyInUriLabel", + "S3EscapePathObjectKeyInUriLabel", + ) private fun fixRestJsonAllQueryStringTypes( testCase: HttpRequestTestCase, @@ -891,33 +902,6 @@ class ServerProtocolTestGenerator( ).asObjectNode().get(), ).build() - private fun fixRestJsonQueryStringEscaping( - testCase: HttpRequestTestCase, - @Suppress("UNUSED_PARAMETER") - operationShape: OperationShape, - ): HttpRequestTestCase = - testCase.toBuilder().params( - Node.parse( - """ - { - "queryString": "%:/?#[]@!${'$'}&'()*+,;=😹", - "queryParamsMapOfStringList": { - "String": ["%:/?#[]@!${'$'}&'()*+,;=😹"] - } - } - """.trimMargin(), - ).asObjectNode().get(), - ).build() - - private fun fixRestJsonInvalidGreetingError(testCase: HttpResponseTestCase): HttpResponseTestCase = - testCase.toBuilder().putHeader("X-Amzn-Errortype", "aws.protocoltests.restjson#InvalidGreeting").build() - - private fun fixRestJsonEmptyComplexErrorWithNoMessage(testCase: HttpResponseTestCase): HttpResponseTestCase = - testCase.toBuilder().putHeader("X-Amzn-Errortype", "aws.protocoltests.restjson#ComplexError").build() - - private fun fixRestJsonComplexErrorWithNoMessage(testCase: HttpResponseTestCase): HttpResponseTestCase = - testCase.toBuilder().putHeader("X-Amzn-Errortype", "aws.protocoltests.restjson#ComplexError").build() - // TODO(https://github.com/awslabs/smithy/issues/1506) private fun fixRestJsonMalformedPatternReDOSString(testCase: HttpMalformedRequestTestCase): HttpMalformedRequestTestCase { val brokenResponse = testCase.response @@ -939,24 +923,11 @@ class ServerProtocolTestGenerator( .build() } - // These are tests whose definitions in the `awslabs/smithy` repository are wrong. - // This is because they have not been written from a server perspective, and as such the expected `params` field is incomplete. - // TODO(https://github.com/awslabs/smithy-rs/issues/1288): Contribute a PR to fix them upstream. - private val BrokenRequestTests = mapOf( - // TODO(https://github.com/awslabs/smithy/pull/1564) - // Pair(RestJson, "RestJsonAllQueryStringTypes") to ::fixRestJsonAllQueryStringTypes, - // TODO(https://github.com/awslabs/smithy/pull/1562) - Pair(RestJson, "RestJsonQueryStringEscaping") to ::fixRestJsonQueryStringEscaping, - ) - - private val BrokenResponseTests: Map, KFunction1> = - // TODO(https://github.com/awslabs/smithy/issues/1494) - mapOf( - Pair(RestJson, "RestJsonInvalidGreetingError") to ::fixRestJsonInvalidGreetingError, - Pair(RestJson, "RestJsonEmptyComplexErrorWithNoMessage") to ::fixRestJsonEmptyComplexErrorWithNoMessage, - Pair(RestJson, "RestJsonComplexErrorWithNoMessage") to ::fixRestJsonComplexErrorWithNoMessage, - ) - + // TODO(https://github.com/awslabs/smithy-rs/issues/1288): Move the fixed versions into + // `rest-json-extras.smithy` and put the unfixed ones in `ExpectFail`: this has the + // advantage that once our upstream PRs get merged and we upgrade to the next Smithy release, our build will + // fail and we will take notice to remove the fixes from `rest-json-extras.smithy`. This is exactly what the + // client does. private val BrokenMalformedRequestTests: Map, KFunction1> = // TODO(https://github.com/awslabs/smithy/issues/1506) mapOf( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index 2a3467cd6b..549ca88b1e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -10,11 +10,13 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.escape import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.protocols.awsJsonFieldName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerSection @@ -30,13 +32,22 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.Ser * AwsJson 1.0 and 1.1 server-side protocol factory. This factory creates the [ServerHttpBoundProtocolGenerator] * with AwsJson specific configurations. */ -class ServerAwsJsonFactory(private val version: AwsJsonVersion) : - ProtocolGeneratorFactory { +class ServerAwsJsonFactory( + private val version: AwsJsonVersion, + private val additionalParserCustomizations: List = listOf(), + private val additionalServerHttpBoundProtocolCustomizations: List = listOf(), + private val additionalHttpBindingCustomizations: List = listOf(), +) : ProtocolGeneratorFactory { override fun protocol(codegenContext: ServerCodegenContext): ServerProtocol = - ServerAwsJsonProtocol(codegenContext, version) + ServerAwsJsonProtocol(codegenContext, version, additionalParserCustomizations) override fun buildProtocolGenerator(codegenContext: ServerCodegenContext): ServerHttpBoundProtocolGenerator = - ServerHttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) + ServerHttpBoundProtocolGenerator( + codegenContext, + protocol(codegenContext), + additionalServerHttpBoundProtocolCustomizations, + additionalHttpBindingCustomizations, + ) override fun support(): ProtocolSupport { return ProtocolSupport( @@ -57,6 +68,14 @@ class ServerAwsJsonFactory(private val version: AwsJsonVersion) : /** * AwsJson requires errors to be serialized in server responses with an additional `__type` field. This * customization writes the right field depending on the version of the AwsJson protocol. + * + * From the specs: + * - https://smithy.io/2.0/aws/protocols/aws-json-1_0-protocol.html#operation-error-serialization + * - https://smithy.io/2.0/aws/protocols/aws-json-1_1-protocol.html#operation-error-serialization + * + * > Error responses in the protocol are serialized identically to standard responses with one additional + * > component to distinguish which error is contained. New server-side protocol implementations SHOULD use a body + * > field named __type */ class ServerAwsJsonError(private val awsJsonVersion: AwsJsonVersion) : JsonSerializerCustomization() { override fun section(section: JsonSerializerSection): Writable = when (section) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index e0e5897fd9..af0caf6b47 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.BooleanShape import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.NumberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape @@ -40,17 +41,21 @@ import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator @@ -77,6 +82,18 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.Ser import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import java.util.logging.Logger +/** + * Class describing a ServerHttpBoundProtocol section that can be used in a customization. + */ +sealed class ServerHttpBoundProtocolSection(name: String) : Section(name) { + data class AfterTimestampDeserializedMember(val shape: MemberShape) : ServerHttpBoundProtocolSection("AfterTimestampDeserializedMember") +} + +/** + * Customization for the ServerHttpBoundProtocol generator. + */ +typealias ServerHttpBoundProtocolCustomization = NamedCustomization + /** * Implement operations' input parsing and output serialization. Protocols can plug their own implementations * and overrides by creating a protocol factory inheriting from this class and feeding it to the [ServerProtocolLoader]. @@ -85,10 +102,11 @@ import java.util.logging.Logger class ServerHttpBoundProtocolGenerator( codegenContext: ServerCodegenContext, protocol: ServerProtocol, + customizations: List = listOf(), + additionalHttpBindingCustomizations: List = listOf(), ) : ServerProtocolGenerator( - codegenContext, protocol, - ServerHttpBoundProtocolTraitImplGenerator(codegenContext, protocol), + ServerHttpBoundProtocolTraitImplGenerator(codegenContext, protocol, customizations, additionalHttpBindingCustomizations), ) { // Define suffixes for operation input / output / error wrappers companion object { @@ -97,6 +115,31 @@ class ServerHttpBoundProtocolGenerator( } } +class ServerHttpBoundProtocolPayloadGenerator( + codegenContext: CodegenContext, + protocol: Protocol, +) : ProtocolPayloadGenerator by HttpBoundProtocolPayloadGenerator( + codegenContext, protocol, HttpMessageType.RESPONSE, + renderEventStreamBody = { writer, params -> + writer.rustTemplate( + """ + { + let error_marshaller = #{errorMarshallerConstructorFn}(); + let marshaller = #{marshallerConstructorFn}(); + let signer = #{NoOpSigner}{}; + let adapter: #{aws_smithy_http}::event_stream::MessageStreamAdapter<_, _> = + ${params.outerName}.${params.memberName}.into_body_stream(marshaller, error_marshaller, signer); + adapter + } + """, + "aws_smithy_http" to RuntimeType.smithyHttp(codegenContext.runtimeConfig), + "NoOpSigner" to RuntimeType.smithyEventStream(codegenContext.runtimeConfig).resolve("frame::NoOpSigner"), + "marshallerConstructorFn" to params.marshallerConstructorFn, + "errorMarshallerConstructorFn" to params.errorMarshallerConstructorFn, + ) + }, +) + /* * Generate all operation input parsers and output serializers for streaming and * non-streaming types. @@ -104,6 +147,8 @@ class ServerHttpBoundProtocolGenerator( class ServerHttpBoundProtocolTraitImplGenerator( private val codegenContext: ServerCodegenContext, private val protocol: ServerProtocol, + private val customizations: List, + private val additionalHttpBindingCustomizations: List, ) { private val logger = Logger.getLogger(javaClass.name) private val symbolProvider = codegenContext.symbolProvider @@ -111,7 +156,6 @@ class ServerHttpBoundProtocolTraitImplGenerator( private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val httpBindingResolver = protocol.httpBindingResolver - private val typeConversionGenerator = TypeConversionGenerator(model, symbolProvider, runtimeConfig) private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( @@ -119,6 +163,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( "Cow" to RuntimeType.Cow, "DateTime" to RuntimeType.dateTime(runtimeConfig), "FormUrlEncoded" to ServerCargoDependency.FormUrlEncoded.toType(), + "FuturesUtil" to ServerCargoDependency.FuturesUtil.toType(), "HttpBody" to RuntimeType.HttpBody, "header_util" to RuntimeType.smithyHttp(runtimeConfig).resolve("header"), "Hyper" to RuntimeType.Hyper, @@ -135,9 +180,10 @@ class ServerHttpBoundProtocolTraitImplGenerator( "ResponseRejection" to protocol.responseRejection(runtimeConfig), "PinProjectLite" to ServerCargoDependency.PinProjectLite.toType(), "http" to RuntimeType.Http, + "Tracing" to RuntimeType.Tracing, ) - fun generateTraitImpls(operationWriter: RustWriter, operationShape: OperationShape, customizations: List) { + fun generateTraitImpls(operationWriter: RustWriter, operationShape: OperationShape) { val inputSymbol = symbolProvider.toSymbol(operationShape.inputShape(model)) val outputSymbol = symbolProvider.toSymbol(operationShape.outputShape(model)) @@ -158,18 +204,36 @@ class ServerHttpBoundProtocolTraitImplGenerator( outputSymbol: Symbol, operationShape: OperationShape, ) { + val operationName = symbolProvider.toSymbol(operationShape).name + val staticContentType = "CONTENT_TYPE_${operationName.uppercase()}" val verifyAcceptHeader = writable { httpBindingResolver.responseContentType(operationShape)?.also { contentType -> rustTemplate( """ - if !#{SmithyHttpServer}::protocols::accept_header_classifier(request.headers(), ${contentType.dq()}) { - return Err(#{RuntimeError}::NotAcceptable) + if !#{SmithyHttpServer}::protocol::accept_header_classifier(request.headers(), &$staticContentType) { + return Err(#{RequestRejection}::NotAcceptable); } """, *codegenScope, ) } } + val verifyAcceptHeaderStaticContentTypeInit = writable { + httpBindingResolver.responseContentType(operationShape)?.also { contentType -> + val init = when (contentType) { + "application/json" -> "const $staticContentType: #{Mime}::Mime = #{Mime}::APPLICATION_JSON;" + "application/octet-stream" -> "const $staticContentType: #{Mime}::Mime = #{Mime}::APPLICATION_OCTET_STREAM;" + "application/x-www-form-urlencoded" -> "const $staticContentType: #{Mime}::Mime = #{Mime}::APPLICATION_WWW_FORM_URLENCODED;" + else -> + """ + static $staticContentType: #{OnceCell}::sync::Lazy<#{Mime}::Mime> = #{OnceCell}::sync::Lazy::new(|| { + ${contentType.dq()}.parse::<#{Mime}::Mime>().expect("BUG: MIME parsing failed, content_type is not valid") + }); + """ + } + rustTemplate(init, *codegenScope) + } + } // This checks for the expected `Content-Type` header if the `@httpPayload` trait is present, as dictated by // the core Smithy library, which _does not_ require deserializing the payload. // If no members have `@httpPayload`, the expected `Content-Type` header as dictated _by the protocol_ is @@ -188,9 +252,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( val expectedRequestContentType = httpBindingResolver.requestContentType(operationShape)!! rustTemplate( """ - if #{SmithyHttpServer}::protocols::content_type_header_classifier(request.headers(), Some("$expectedRequestContentType")).is_err() { - return Err(#{RuntimeError}::UnsupportedMediaType); - } + #{SmithyHttpServer}::protocol::content_type_header_classifier(request.headers(), $expectedRequestContentType)?; """, *codegenScope, ) @@ -200,9 +262,10 @@ class ServerHttpBoundProtocolTraitImplGenerator( // Implement `from_request` trait for input types. val inputFuture = "${inputSymbol.name}Future" + // TODO(https://github.com/awslabs/smithy-rs/issues/2238): Remove the `Pin>` and replace with thin wrapper around `Collect`. rustTemplate( """ - // TODO(https://github.com/awslabs/smithy-rs/issues/2238): Remove the `Pin>` and replace with thin wrapper around `Collect`. + #{verifyAcceptHeaderStaticContentTypeInit:W} #{PinProjectLite}::pin_project! { /// A [`Future`](std::future::Future) aggregating the body bytes of a [`Request`] and constructing the /// [`${inputSymbol.name}`](#{I}) using modelled bindings. @@ -239,35 +302,45 @@ class ServerHttpBoundProtocolTraitImplGenerator( .await .map_err(Into::into) }; + use #{FuturesUtil}::future::TryFutureExt; + let fut = fut.map_err(|e: #{RequestRejection}| { + #{Tracing}::debug!(error = %e, "failed to deserialize request"); + #{RuntimeError}::from(e) + }); $inputFuture { inner: Box::pin(fut) } } } - - """.trimIndent(), + """, *codegenScope, "I" to inputSymbol, "Marker" to protocol.markerStruct(), "parse_request" to serverParseRequest(operationShape), "verifyAcceptHeader" to verifyAcceptHeader, + "verifyAcceptHeaderStaticContentTypeInit" to verifyAcceptHeaderStaticContentTypeInit, "verifyRequestContentTypeHeader" to verifyRequestContentTypeHeader, ) // Implement `into_response` for output types. val errorSymbol = symbolProvider.symbolForOperationError(operationShape) + // All `ResponseRejection`s are errors; the service owners are to blame. So we centrally log them here + // to let them know. rustTemplate( """ impl #{SmithyHttpServer}::response::IntoResponse<#{Marker}> for #{O} { fn into_response(self) -> #{SmithyHttpServer}::response::Response { match #{serialize_response}(self) { Ok(response) => response, - Err(e) => #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + Err(e) => { + #{Tracing}::error!(error = %e, "failed to serialize response"); + #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + } } } } - """.trimIndent(), + """, *codegenScope, "O" to outputSymbol, "Marker" to protocol.markerStruct(), @@ -284,7 +357,10 @@ class ServerHttpBoundProtocolTraitImplGenerator( response.extensions_mut().insert(#{SmithyHttpServer}::extension::ModeledErrorExtension::new(self.name())); response }, - Err(e) => #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + Err(e) => { + #{Tracing}::error!(error = %e, "failed to serialize response"); + #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + } } } } @@ -387,7 +463,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( errorSymbol: Symbol, ) { val operationName = symbolProvider.toSymbol(operationShape).name - val structuredDataSerializer = protocol.structuredDataSerializer(operationShape) + val structuredDataSerializer = protocol.structuredDataSerializer() withBlock("match error {", "}") { val errors = operationShape.operationErrors(model) errors.forEach { @@ -445,26 +521,27 @@ class ServerHttpBoundProtocolTraitImplGenerator( Attribute.AllowUnusedMut.render(this) rustTemplate("let mut builder = #{http}::Response::builder();", *codegenScope) serverRenderResponseHeaders(operationShape) - // Fallback to the default code of `@http`, 200. + // Fallback to the default code of `@http`, which should be 200. val httpTraitDefaultStatusCode = HttpTrait .builder().method("GET").uri(UriPattern.parse("/")) /* Required to build */ .build() .code + check(httpTraitDefaultStatusCode == 200) val httpTraitStatusCode = operationShape.getTrait()?.code ?: httpTraitDefaultStatusCode bindings.find { it.location == HttpLocation.RESPONSE_CODE } ?.let { serverRenderResponseCodeBinding(it, httpTraitStatusCode)(this) } - // no binding, use http's + // No binding, use `@http`. ?: serverRenderHttpResponseCode(httpTraitStatusCode)(this) operationShape.outputShape(model).findStreamingMember(model)?.let { - val payloadGenerator = HttpBoundProtocolPayloadGenerator(codegenContext, protocol, httpMessageType = HttpMessageType.RESPONSE) + val payloadGenerator = ServerHttpBoundProtocolPayloadGenerator(codegenContext, protocol) withBlockTemplate("let body = #{SmithyHttpServer}::body::boxed(#{SmithyHttpServer}::body::Body::wrap_stream(", "));", *codegenScope) { payloadGenerator.generatePayload(this, "output", operationShape) } } ?: run { - val payloadGenerator = HttpBoundProtocolPayloadGenerator(codegenContext, protocol, httpMessageType = HttpMessageType.RESPONSE) + val payloadGenerator = ServerHttpBoundProtocolPayloadGenerator(codegenContext, protocol) withBlockTemplate("let payload = ", ";") { payloadGenerator.generatePayload(this, "output", operationShape) } @@ -561,46 +638,42 @@ class ServerHttpBoundProtocolTraitImplGenerator( ) } - private fun serverRenderHttpResponseCode( - defaultCode: Int, - ): Writable { - return writable { - rustTemplate( - """ - let status = $defaultCode; - let http_status: u16 = status.try_into() - .map_err(|_| #{ResponseRejection}::InvalidHttpStatusCode)?; - builder = builder.status(http_status); - """.trimIndent(), - *codegenScope, - ) + private fun serverRenderHttpResponseCode(defaultCode: Int) = writable { + check(defaultCode in 100..999) { + """ + Smithy library lied to us. According to https://smithy.io/2.0/spec/http-bindings.html#http-trait, + "The provided value SHOULD be between 100 and 599, and it MUST be between 100 and 999". + """.replace("\n", "").trimIndent() } + rustTemplate( + """ + let http_status: u16 = $defaultCode; + builder = builder.status(http_status); + """, + *codegenScope, + ) } private fun serverRenderResponseCodeBinding( binding: HttpBindingDescriptor, + /** This is the status code to fall back on if the member shape bound with `@httpResponseCode` is not + * `@required` and the user did not provide a value for it at runtime. **/ fallbackStatusCode: Int, ): Writable { check(binding.location == HttpLocation.RESPONSE_CODE) return writable { val memberName = symbolProvider.toMemberName(binding.member) - rust("let status = output.$memberName") - if (symbolProvider.toSymbol(binding.member).isOptional()) { - rustTemplate( - """ - .unwrap_or($fallbackStatusCode) - """.trimIndent(), - *codegenScope, - ) + withBlock("let status = output.$memberName", ";") { + if (symbolProvider.toSymbol(binding.member).isOptional()) { + rust(".unwrap_or($fallbackStatusCode)") + } } rustTemplate( """ - ; - let http_status: u16 = status.try_into() - .map_err(|_| #{ResponseRejection}::InvalidHttpStatusCode)?; + let http_status: u16 = status.try_into().map_err(#{ResponseRejection}::InvalidHttpStatusCode)?; builder = builder.status(http_status); - """.trimIndent(), + """, *codegenScope, ) } @@ -611,8 +684,8 @@ class ServerHttpBoundProtocolTraitImplGenerator( inputShape: StructureShape, bindings: List, ) { - val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) - val structuredDataParser = protocol.structuredDataParser(operationShape) + val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape, additionalHttpBindingCustomizations) + val structuredDataParser = protocol.structuredDataParser() Attribute.AllowUnusedMut.render(this) rust( "let mut input = #T::default();", @@ -631,7 +704,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( rustBlock("if !bytes.is_empty()") { rustTemplate( """ - #{SmithyHttpServer}::protocols::content_type_header_classifier(&parts.headers, Some("$expectedRequestContentType"))?; + #{SmithyHttpServer}::protocol::content_type_header_classifier(&parts.headers, Some("$expectedRequestContentType"))?; input = #{parser}(bytes.as_ref(), input)?; """, *codegenScope, @@ -667,7 +740,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( conditionalBlock("if body.is_empty() {", "}", conditional = parser != null) { rustTemplate( """ - #{SmithyHttpServer}::protocols::content_type_header_empty_body_no_modeled_input(&parts.headers)?; + #{SmithyHttpServer}::protocol::content_type_header_empty_body_no_modeled_input(&parts.headers)?; """, *codegenScope, ) @@ -896,7 +969,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( val (queryBindingsTargetingCollection, queryBindingsTargetingSimple) = queryBindings.partition { model.expectShape(it.member.target) is CollectionShape } queryBindingsTargetingSimple.forEach { - rust("let mut seen_${symbolProvider.toMemberName(it.member)} = false;") + rust("let mut ${symbolProvider.toMemberName(it.member)}_seen = false;") } queryBindingsTargetingCollection.forEach { rust("let mut ${symbolProvider.toMemberName(it.member)} = Vec::new();") @@ -908,11 +981,11 @@ class ServerHttpBoundProtocolTraitImplGenerator( val memberName = symbolProvider.toMemberName(it.member) rustTemplate( """ - if !seen_$memberName && k == "${it.locationName}" { + if !${memberName}_seen && k == "${it.locationName}" { input = input.${it.member.setterName()}( #{deserializer}(&v)? ); - seen_$memberName = true; + ${memberName}_seen = true; } """.trimIndent(), "deserializer" to deserializer, @@ -944,12 +1017,15 @@ class ServerHttpBoundProtocolTraitImplGenerator( val timestampFormatType = RuntimeType.parseTimestampFormat(CodegenTarget.SERVER, runtimeConfig, timestampFormat) rustTemplate( """ - let v = #{DateTime}::from_str(&v, #{format})?#{ConvertInto:W}; + let v = #{DateTime}::from_str(&v, #{format})? """.trimIndent(), *codegenScope, "format" to timestampFormatType, - "ConvertInto" to typeConversionGenerator.convertViaInto(memberShape), ) + for (customization in customizations) { + customization.section(ServerHttpBoundProtocolSection.AfterTimestampDeserializedMember(it.member))(this) + } + rust(";") } else -> { // Number or boolean. rust( @@ -1039,7 +1115,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( } private fun serverRenderHeaderParser(writer: RustWriter, binding: HttpBindingDescriptor, operationShape: OperationShape) { - val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) + val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape, additionalHttpBindingCustomizations) val deserializer = httpBindingGenerator.generateDeserializeHeaderFn(binding) writer.rustTemplate( """ @@ -1101,22 +1177,24 @@ class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ let value = #{PercentEncoding}::percent_decode_str(value).decode_utf8()?; - let value = #{DateTime}::from_str(value.as_ref(), #{format})?#{ConvertInto:W}; + let value = #{DateTime}::from_str(value.as_ref(), #{format})? """, *codegenScope, "format" to timestampFormatType, - "ConvertInto" to typeConversionGenerator.convertViaInto(target), ) } else { rustTemplate( """ - let value = #{DateTime}::from_str(value, #{format})?#{ConvertInto:W}; + let value = #{DateTime}::from_str(value, #{format})? """, *codegenScope, "format" to timestampFormatType, - "ConvertInto" to typeConversionGenerator.convertViaInto(target), ) } + for (customization in customizations) { + customization.section(ServerHttpBoundProtocolSection.AfterTimestampDeserializedMember(binding.member))(this) + } + rust(";") } else -> { check(target is NumberShape || target is BooleanShape) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt index 744ef93bc5..ddf1ca08c3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt @@ -5,10 +5,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.restJsonFieldName import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator @@ -21,11 +23,23 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.Ser * RestJson1 server-side protocol factory. This factory creates the [ServerHttpProtocolGenerator] * with RestJson1 specific configurations. */ -class ServerRestJsonFactory : ProtocolGeneratorFactory { - override fun protocol(codegenContext: ServerCodegenContext): Protocol = ServerRestJsonProtocol(codegenContext) +class ServerRestJsonFactory( + private val additionalParserCustomizations: List = listOf(), + private val additionalServerHttpBoundProtocolCustomizations: List = listOf(), + private val additionalHttpBindingCustomizations: List = listOf(), +) : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ServerCodegenContext): Protocol = ServerRestJsonProtocol(codegenContext, additionalParserCustomizations) override fun buildProtocolGenerator(codegenContext: ServerCodegenContext): ServerHttpBoundProtocolGenerator = - ServerHttpBoundProtocolGenerator(codegenContext, ServerRestJsonProtocol(codegenContext)) + ServerHttpBoundProtocolGenerator( + codegenContext, + ServerRestJsonProtocol( + codegenContext, + additionalParserCustomizations, + ), + additionalServerHttpBoundProtocolCustomizations, + additionalHttpBindingCustomizations, + ) override fun support(): ProtocolSupport { return ProtocolSupport( diff --git a/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator b/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator new file mode 100644 index 0000000000..753c72d1ce --- /dev/null +++ b/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator @@ -0,0 +1,5 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# +software.amazon.smithy.rust.codegen.server.smithy.PatternTraitEscapedSpecialCharsValidator diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/NamingObstacleCourseTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/NamingObstacleCourseTest.kt new file mode 100644 index 0000000000..b0ae1c3473 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/NamingObstacleCourseTest.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest + +class NamingObstacleCourseTest { + @Test + fun `test Rust prelude operation names compile`() { + serverIntegrationTest(NamingObstacleCourseTestModels.rustPreludeOperationsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude structure names compile`() { + serverIntegrationTest(NamingObstacleCourseTestModels.rustPreludeStructsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude enum names compile`() { + serverIntegrationTest(NamingObstacleCourseTestModels.rustPreludeEnumsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude enum variant names compile`() { + serverIntegrationTest(NamingObstacleCourseTestModels.rustPreludeEnumVariantsModel()) { _, _ -> } + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidatorTest.kt new file mode 100644 index 0000000000..042aba7159 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidatorTest.kt @@ -0,0 +1,123 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.validation.Severity +import software.amazon.smithy.model.validation.ValidatedResultException +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel + +class PatternTraitEscapedSpecialCharsValidatorTest { + @Test + fun `should error out with a suggestion if non-escaped special chars used inside @pattern`() { + val exception = shouldThrow { + """ + namespace test + + @pattern("\t") + string MyString + """.asSmithyModel(smithyVersion = "2") + } + val events = exception.validationEvents.filter { it.severity == Severity.ERROR } + + events shouldHaveSize 1 + events[0].shapeId.get() shouldBe ShapeId.from("test#MyString") + events[0].message shouldBe """ + Non-escaped special characters used inside `@pattern`. + You must escape them: `@pattern("\\t")`. + See https://github.com/awslabs/smithy-rs/issues/2508 for more details. + """.trimIndent() + } + + @Test + fun `should suggest escaping spacial characters properly`() { + val exception = shouldThrow { + """ + namespace test + + @pattern("[.\n\\r]+") + string MyString + """.asSmithyModel(smithyVersion = "2") + } + val events = exception.validationEvents.filter { it.severity == Severity.ERROR } + + events shouldHaveSize 1 + events[0].shapeId.get() shouldBe ShapeId.from("test#MyString") + events[0].message shouldBe """ + Non-escaped special characters used inside `@pattern`. + You must escape them: `@pattern("[.\\n\\r]+")`. + See https://github.com/awslabs/smithy-rs/issues/2508 for more details. + """.trimIndent() + } + + @Test + fun `should report all non-escaped special characters`() { + val exception = shouldThrow { + """ + namespace test + + @pattern("\b") + string MyString + + @pattern("^\n$") + string MyString2 + + @pattern("^[\n]+$") + string MyString3 + + @pattern("^[\r\t]$") + string MyString4 + """.asSmithyModel(smithyVersion = "2") + } + val events = exception.validationEvents.filter { it.severity == Severity.ERROR } + events shouldHaveSize 4 + } + + @Test + fun `should report errors on string members`() { + val exception = shouldThrow { + """ + namespace test + + @pattern("\t") + string MyString + + structure MyStructure { + @pattern("\b") + field: String + } + """.asSmithyModel(smithyVersion = "2") + } + val events = exception.validationEvents.filter { it.severity == Severity.ERROR } + + events shouldHaveSize 2 + events[0].shapeId.get() shouldBe ShapeId.from("test#MyString") + events[1].shapeId.get() shouldBe ShapeId.from("test#MyStructure\$field") + } + + @Test + fun `shouldn't error out if special chars are properly escaped`() { + """ + namespace test + + @pattern("\\t") + string MyString + + @pattern("[.\\n\\r]+") + string MyString2 + + @pattern("\\b\\f\\n\\r\\t") + string MyString3 + + @pattern("\\w+") + string MyString4 + """.asSmithyModel(smithyVersion = "2") + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index 7c8efe9c17..5f47e10779 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -98,6 +98,6 @@ class UnconstrainedShapeSymbolProviderTest { val structureBShape = model.lookup("test#StructureB") unconstrainedShapeSymbolProvider.toSymbol(structureBShape).rustType().render() shouldBe "crate::model::StructureB" - unconstrainedShapeSymbolProvider.toSymbol(listAShape).rustType().render() shouldBe "std::vec::Vec" + unconstrainedShapeSymbolProvider.toSymbol(listAShape).rustType().render() shouldBe "::std::vec::Vec" } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt index 68b88a7978..a140322e36 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamN import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator +import java.io.File import java.util.logging.Level internal class ValidateUnsupportedConstraintsAreNotUsedTest { @@ -37,7 +38,7 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { """ private fun validateModel(model: Model, serverCodegenConfig: ServerCodegenConfig = ServerCodegenConfig()): ValidationResult { - val service = model.lookup("test#TestService") + val service = model.serviceShapes.first() return validateUnsupportedConstraints(model, service, serverCodegenConfig) } @@ -100,7 +101,7 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { """.trimIndent().replace("\n", " ") } - val constrainedShapesInEventStreamModel = + private val constrainedShapesInEventStreamModel = """ $baseModel @@ -242,4 +243,25 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { validationResult.messages shouldHaveAtLeastSize 1 validationResult.messages.shouldForAll { it.level shouldBe Level.WARNING } } + + @Test + fun `it should abort when ignoreUnsupportedConstraints is true and all used constraints are supported`() { + val allConstraintTraitsAreSupported = File("../codegen-core/common-test-models/constraints.smithy") + .readText() + .asSmithyModel() + + val validationResult = validateModel( + allConstraintTraitsAreSupported, + ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true), + ) + + validationResult.messages shouldHaveSize 1 + validationResult.shouldAbort shouldBe true + validationResult.messages[0].message shouldContain( + """ + The `ignoreUnsupportedConstraints` flag in the `codegen` configuration is set to `true`, but it has no + effect. All the constraint traits used in the model are well-supported, please remove this flag. + """.trimIndent().replace("\n", " ") + ) + } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt index 406a1dbfea..d7783c7ac1 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestRustSettings class AdditionalErrorsDecoratorTest { private val baseModel = """ @@ -35,12 +36,13 @@ class AdditionalErrorsDecoratorTest { """.asSmithyModel() private val model = OperationNormalizer.transform(baseModel) private val service = ServiceShape.builder().id("smithy.test#Test").build() + private val settings = serverTestRustSettings() @Test fun `add InternalServerError to infallible operations only`() { model.lookup("test#Infallible").errors.isEmpty() shouldBe true model.lookup("test#Fallible").errors.size shouldBe 1 - val transformedModel = AddInternalServerErrorToInfallibleOperationsDecorator().transformModel(service, model) + val transformedModel = AddInternalServerErrorToInfallibleOperationsDecorator().transformModel(service, model, settings) transformedModel.lookup("test#Infallible").errors.size shouldBe 1 transformedModel.lookup("test#Fallible").errors.size shouldBe 1 } @@ -49,7 +51,7 @@ class AdditionalErrorsDecoratorTest { fun `add InternalServerError to all model operations`() { model.lookup("test#Infallible").errors.isEmpty() shouldBe true model.lookup("test#Fallible").errors.size shouldBe 1 - val transformedModel = AddInternalServerErrorToAllOperationsDecorator().transformModel(service, model) + val transformedModel = AddInternalServerErrorToAllOperationsDecorator().transformModel(service, model, settings) transformedModel.lookup("test#Infallible").errors.size shouldBe 1 transformedModel.lookup("test#Fallible").errors.size shouldBe 2 } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGeneratorTest.kt index 060e0166a4..3a35120f83 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGeneratorTest.kt @@ -117,7 +117,7 @@ class ConstrainedBlobGeneratorTest { } @Test - fun `type should not be constructible without using a constructor`() { + fun `type should not be constructable without using a constructor`() { val model = """ namespace test @@ -139,6 +139,6 @@ class ConstrainedBlobGeneratorTest { ).render() // Check that the wrapped type is `pub(crate)`. - writer.toString() shouldContain "pub struct ConstrainedBlob(pub(crate) aws_smithy_types::Blob);" + writer.toString() shouldContain "pub struct ConstrainedBlob(pub(crate) ::aws_smithy_types::Blob);" } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGeneratorTest.kt index cce7da4aa6..3b9e0d4a7b 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGeneratorTest.kt @@ -258,7 +258,7 @@ class ConstrainedCollectionGeneratorTest { } @Test - fun `type should not be constructible without using a constructor`() { + fun `type should not be constructable without using a constructor`() { val model = """ namespace test @@ -276,7 +276,7 @@ class ConstrainedCollectionGeneratorTest { render(codegenContext, writer, constrainedCollectionShape) // Check that the wrapped type is `pub(crate)`. - writer.toString() shouldContain "pub struct ConstrainedList(pub(crate) std::vec::Vec);" + writer.toString() shouldContain "pub struct ConstrainedList(pub(crate) ::std::vec::Vec<::std::string::String>);" } private fun render( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index 0eebb7e36b..cd83336124 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -128,7 +128,7 @@ class ConstrainedMapGeneratorTest { } @Test - fun `type should not be constructible without using a constructor`() { + fun `type should not be constructable without using a constructor`() { val model = """ namespace test @@ -146,7 +146,7 @@ class ConstrainedMapGeneratorTest { render(codegenContext, writer, constrainedMapShape) // Check that the wrapped type is `pub(crate)`. - writer.toString() shouldContain "pub struct ConstrainedMap(pub(crate) std::collections::HashMap);" + writer.toString() shouldContain "pub struct ConstrainedMap(pub(crate) ::std::collections::HashMap<::std::string::String, ::std::string::String>);" } private fun render( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGeneratorTest.kt index 5a78574c93..3a34c7753c 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGeneratorTest.kt @@ -127,7 +127,7 @@ class ConstrainedNumberGeneratorTest { @ParameterizedTest @ArgumentsSource(NoStructuralConstructorTestProvider::class) - fun `type should not be constructible without using a constructor`(args: Triple) { + fun `type should not be constructable without using a constructor`(args: Triple) { val (smithyType, shapeName, rustType) = args val model = """ namespace test diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index 62a7d061c3..5e2f828e1b 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -20,7 +20,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule import software.amazon.smithy.rust.codegen.server.smithy.createTestInlineModuleCreator @@ -131,7 +131,7 @@ class ConstrainedStringGeneratorTest { } @Test - fun `type should not be constructible without using a constructor`() { + fun `type should not be constructable without using a constructor`() { val model = """ namespace test @@ -153,7 +153,7 @@ class ConstrainedStringGeneratorTest { ).render() // Check that the wrapped type is `pub(crate)`. - writer.toString() shouldContain "pub struct ConstrainedString(pub(crate) std::string::String);" + writer.toString() shouldContain "pub struct ConstrainedString(pub(crate) ::std::string::String);" } @Test @@ -237,7 +237,7 @@ class ConstrainedStringGeneratorTest { ).render() } - assertThrows { + assertThrows { project.compileAndTest() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGeneratorTest.kt index 7d5636a151..a73466277a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGeneratorTest.kt @@ -21,9 +21,9 @@ import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymb class ServerOperationErrorGeneratorTest { private val baseModel = """ namespace error - + use aws.protocols#restJson1 - + @restJson1 service MyService { operations: [Greeting] diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 7da7a67e62..feceb42209 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -6,20 +6,11 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test -import software.amazon.smithy.model.shapes.ListShape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.testModule import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.lookup -import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule -import software.amazon.smithy.rust.codegen.server.smithy.createTestInlineModuleCreator -import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerRestJsonProtocol -import software.amazon.smithy.rust.codegen.server.smithy.renderInlineMemoryModules -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest class UnconstrainedCollectionGeneratorTest { @Test @@ -28,6 +19,25 @@ class UnconstrainedCollectionGeneratorTest { """ namespace test + use aws.protocols#restJson1 + use smithy.framework#ValidationException + + @restJson1 + service TestService { + operations: ["Operation"] + } + + @http(uri: "/operation", method: "POST") + operation Operation { + input: OperationInputOutput + output: OperationInputOutput + errors: [ValidationException] + } + + structure OperationInputOutput { + list: ListA + } + list ListA { member: ListB } @@ -44,58 +54,16 @@ class UnconstrainedCollectionGeneratorTest { string: String } """.asSmithyModel() - val codegenContext = serverTestCodegenContext(model) - val symbolProvider = codegenContext.symbolProvider - - val listA = model.lookup("test#ListA") - val listB = model.lookup("test#ListB") - - val project = TestWorkspace.testProject(symbolProvider) - - project.withModule(ServerRustModule.Model) { - model.lookup("test#StructureC").serverRenderWithModelBuilder( - project, - model, - symbolProvider, - this, - ServerRestJsonProtocol(codegenContext), - ) - } - project.withModule(ServerRustModule.ConstrainedModule) { - listOf(listA, listB).forEach { - PubCrateConstrainedCollectionGenerator( - codegenContext, - this.createTestInlineModuleCreator(), - it, - ).render() - } - } - project.withModule(ServerRustModule.UnconstrainedModule) unconstrainedModuleWriter@{ - project.withModule(ServerRustModule.Model) modelsModuleWriter@{ - listOf(listA, listB).forEach { - UnconstrainedCollectionGenerator( - codegenContext, - this@unconstrainedModuleWriter.createTestInlineModuleCreator(), - it, - ).render() - - CollectionConstraintViolationGenerator( - codegenContext, - this@modelsModuleWriter.createTestInlineModuleCreator(), - it, - CollectionTraitInfo.fromShape(it, codegenContext.constrainedShapeSymbolProvider), - SmithyValidationExceptionConversionGenerator(codegenContext), - ).render() - } - - this@unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_fail_to_constrain_with_first_error", - test = """ + serverIntegrationTest(model) { _, rustCrate -> + rustCrate.testModule { + unitTest("list_a_unconstrained_fail_to_constrain_with_first_error") { + rust( + """ let c_builder1 = crate::model::StructureC::builder().int(69); let c_builder2 = crate::model::StructureC::builder().string("david".to_owned()); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + let list_b_unconstrained = crate::unconstrained::list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); + let list_a_unconstrained = crate::unconstrained::list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let expected_err = crate::model::list_a::ConstraintViolation::Member(0, crate::model::list_b::ConstraintViolation::Member( @@ -106,15 +74,16 @@ class UnconstrainedCollectionGeneratorTest { expected_err, crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() ); - """, - ) + """, + ) + } - this@unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_succeed_to_constrain", - test = """ + unitTest("list_a_unconstrained_succeed_to_constrain") { + rust( + """ let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + let list_b_unconstrained = crate::unconstrained::list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = crate::unconstrained::list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let expected: Vec> = vec![vec![crate::model::StructureC { string: "david".to_owned(), @@ -124,22 +93,22 @@ class UnconstrainedCollectionGeneratorTest { crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); assert_eq!(expected, actual); - """, - ) + """, + ) + } - this@unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_converts_into_constrained", - test = """ + unitTest("list_a_unconstrained_converts_into_constrained") { + rust( + """ let c_builder = crate::model::StructureC::builder(); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + let list_b_unconstrained = crate::unconstrained::list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = crate::unconstrained::list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); - """, - ) + """, + ) + } } } - project.renderInlineMemoryModules() - project.compileAndTest() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 9cc5fe5870..45f8b33fbc 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -7,20 +7,14 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.smithy.CoreCodegenConfig import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.testModule import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup -import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule -import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Model -import software.amazon.smithy.rust.codegen.server.smithy.createTestInlineModuleCreator -import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerRestJsonProtocol -import software.amazon.smithy.rust.codegen.server.smithy.renderInlineMemoryModules -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext class UnconstrainedMapGeneratorTest { @@ -30,6 +24,25 @@ class UnconstrainedMapGeneratorTest { """ namespace test + use aws.protocols#restJson1 + use smithy.framework#ValidationException + + @restJson1 + service TestService { + operations: ["Operation"] + } + + @http(uri: "/operation", method: "POST") + operation Operation { + input: OperationInputOutput + output: OperationInputOutput + errors: [ValidationException] + } + + structure OperationInputOutput { + map: MapA + } + map MapA { key: String, value: MapB @@ -56,53 +69,20 @@ class UnconstrainedMapGeneratorTest { val project = TestWorkspace.testProject(symbolProvider, CoreCodegenConfig(debugMode = true)) - project.withModule(Model) { - model.lookup("test#StructureC").serverRenderWithModelBuilder( - project, - model, - symbolProvider, - this, - ServerRestJsonProtocol(codegenContext), - ) - } - - project.withModule(ServerRustModule.ConstrainedModule) { - listOf(mapA, mapB).forEach { - PubCrateConstrainedMapGenerator( - codegenContext, - this.createTestInlineModuleCreator(), - it, - ).render() - } - } - project.withModule(ServerRustModule.UnconstrainedModule) unconstrainedModuleWriter@{ - project.withModule(Model) modelsModuleWriter@{ - listOf(mapA, mapB).forEach { - UnconstrainedMapGenerator( - codegenContext, - this@unconstrainedModuleWriter.createTestInlineModuleCreator(), it, - ).render() - - MapConstraintViolationGenerator( - codegenContext, - this@modelsModuleWriter.createTestInlineModuleCreator(), - it, - SmithyValidationExceptionConversionGenerator(codegenContext), - ).render() - } - - this@unconstrainedModuleWriter.unitTest( - name = "map_a_unconstrained_fail_to_constrain_with_some_error", - test = """ + serverIntegrationTest(model) { _, rustCrate -> + rustCrate.testModule { + unitTest("map_a_unconstrained_fail_to_constrain_with_some_error") { + rust( + """ let c_builder1 = crate::model::StructureC::builder().int(69); let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); - let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + let map_b_unconstrained = crate::unconstrained::map_b_unconstrained::MapBUnconstrained( std::collections::HashMap::from([ (String::from("KeyB1"), c_builder1), (String::from("KeyB2"), c_builder2), ]) ); - let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + let map_a_unconstrained = crate::unconstrained::map_a_unconstrained::MapAUnconstrained( std::collections::HashMap::from([ (String::from("KeyA"), map_b_unconstrained), ]) @@ -127,19 +107,19 @@ class UnconstrainedMapGeneratorTest { let actual_err = crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap_err(); assert!(actual_err == missing_string_expected_err || actual_err == missing_int_expected_err); - """, - ) - - this@unconstrainedModuleWriter.unitTest( - name = "map_a_unconstrained_succeed_to_constrain", - test = """ + """, + ) + } + unitTest("map_a_unconstrained_succeed_to_constrain") { + rust( + """ let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); - let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + let map_b_unconstrained = crate::unconstrained::map_b_unconstrained::MapBUnconstrained( std::collections::HashMap::from([ (String::from("KeyB"), c_builder), ]) ); - let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + let map_a_unconstrained = crate::unconstrained::map_a_unconstrained::MapAUnconstrained( std::collections::HashMap::from([ (String::from("KeyA"), map_b_unconstrained), ]) @@ -158,30 +138,29 @@ class UnconstrainedMapGeneratorTest { expected, crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap().into() ); - """, - ) - - this@unconstrainedModuleWriter.unitTest( - name = "map_a_unconstrained_converts_into_constrained", - test = """ + """, + ) + } + unitTest("map_a_unconstrained_converts_into_constrained") { + rust( + """ let c_builder = crate::model::StructureC::builder(); - let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + let map_b_unconstrained = crate::unconstrained::map_b_unconstrained::MapBUnconstrained( std::collections::HashMap::from([ (String::from("KeyB"), c_builder), ]) ); - let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + let map_a_unconstrained = crate::unconstrained::map_a_unconstrained::MapAUnconstrained( std::collections::HashMap::from([ (String::from("KeyA"), map_b_unconstrained), ]) ); let _map_a: crate::constrained::MaybeConstrained = map_a_unconstrained.into(); - """, - ) + """, + ) + } } } - project.renderInlineMemoryModules() - project.compileAndTest() } } diff --git a/codegen-server/typescript/build.gradle.kts b/codegen-server/typescript/build.gradle.kts new file mode 100644 index 0000000000..b6dfa39a8f --- /dev/null +++ b/codegen-server/typescript/build.gradle.kts @@ -0,0 +1,86 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import org.gradle.api.tasks.testing.logging.TestExceptionFormat + +plugins { + kotlin("jvm") + `maven-publish` +} + +description = "Generates Rust/Node server-side code from Smithy models" + +extra["displayName"] = "Smithy :: Rust :: Codegen :: Server :: Typescript" + +extra["moduleName"] = "software.amazon.smithy.rust.codegen.server.typescript" + +group = "software.amazon.smithy.rust.codegen.server.typescript.smithy" + +version = "0.1.0" + +val smithyVersion: String by project + +dependencies { + implementation(project(":codegen-core")) + implementation(project(":codegen-server")) + implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") + implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") +} + +tasks.compileKotlin { kotlinOptions.jvmTarget = "1.8" } + +// Reusable license copySpec +val licenseSpec = copySpec { + from("${project.rootDir}/LICENSE") + from("${project.rootDir}/NOTICE") +} + +// Configure jars to include license related info +tasks.jar { + metaInf.with(licenseSpec) + inputs.property("moduleName", project.name) + manifest { attributes["Automatic-Module-Name"] = project.name } +} + +val sourcesJar by tasks.creating(Jar::class) { + group = "publishing" + description = "Assembles Kotlin sources jar" + archiveClassifier.set("sources") + from(sourceSets.getByName("main").allSource) +} + +val isTestingEnabled: String by project +if (isTestingEnabled.toBoolean()) { + val kotestVersion: String by project + + dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:5.6.1") + testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion") + } + + tasks.compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } + + tasks.test { + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + exceptionFormat = TestExceptionFormat.FULL + showCauses = true + showExceptions = true + showStackTraces = true + showStandardStreams = true + } + } +} + +publishing { + publications { + create("default") { + from(components["java"]) + artifact(sourcesJar) + } + } + repositories { maven { url = uri("$buildDir/repository") } } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/RustServerCodegenTsPlugin.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/RustServerCodegenTsPlugin.kt new file mode 100644 index 0000000000..10aafec81b --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/RustServerCodegenTsPlugin.kt @@ -0,0 +1,105 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.build.PluginContext +import software.amazon.smithy.build.SmithyBuildPlugin +import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.BaseSymbolMetadataProvider +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.EventStreamSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor +import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolMetadataProvider +import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.DeriveEqAndHashSymbolMetadataProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerReservedWords +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings +import software.amazon.smithy.rust.codegen.server.smithy.customizations.CustomValidationExceptionWithReasonDecorator +import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations +import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionDecorator +import software.amazon.smithy.rust.codegen.server.smithy.customize.CombinedServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.customizations.DECORATORS +import java.util.logging.Level +import java.util.logging.Logger + +/** + * Rust with Typescript bindings Codegen Plugin. + * This is the entrypoint for code generation, triggered by the smithy-build plugin. + * `resources/META-INF.services/software.amazon.smithy.build.SmithyBuildPlugin` refers to this class by name which + * enables the smithy-build plugin to invoke `execute` with all of the Smithy plugin context + models. + */ +class RustServerCodegenTsPlugin : SmithyBuildPlugin { + private val logger = Logger.getLogger(javaClass.name) + + override fun getName(): String = "rust-server-codegen-typescript" + + override fun execute(context: PluginContext) { + // Suppress extremely noisy logs about reserved words + Logger.getLogger(ReservedWordSymbolProvider::class.java.name).level = Level.OFF + // Discover [RustCodegenDecorators] on the classpath. [RustCodegenDecorator] return different types of + // customization. A customization is a function of: + // - location (e.g. the mutate section of an operation) + // - context (e.g. the of the operation) + // - writer: The active RustWriter at the given location + val codegenDecorator: CombinedServerCodegenDecorator = + CombinedServerCodegenDecorator.fromClasspath( + context, + ServerRequiredCustomizations(), + SmithyValidationExceptionDecorator(), + CustomValidationExceptionWithReasonDecorator(), + *DECORATORS, + ) + + // TsServerCodegenVisitor is the main driver of code generation that traverses the model and generates code + logger.info("Loaded plugin to generate Rust/Node bindings for the server SSDK for projection ${context.projectionName}") + TsServerCodegenVisitor(context, codegenDecorator).execute() + } + + companion object { + /** + * When generating code, smithy types need to be converted into Rust types—that is the core role of the symbol provider + * + * The Symbol provider is composed of a base [SymbolVisitor] which handles the core functionality, then is layered + * with other symbol providers, documented inline, to handle the full scope of Smithy types. + */ + fun baseSymbolProvider( + settings: ServerRustSettings, + model: Model, + serviceShape: ServiceShape, + rustSymbolProviderConfig: RustSymbolProviderConfig, + constrainedTypes: Boolean = true, + includeConstrainedShapeProvider: Boolean = true, + codegenDecorator: ServerCodegenDecorator, + ) = + TsServerSymbolVisitor(settings, model, serviceShape = serviceShape, config = rustSymbolProviderConfig) + // Generate public constrained types for directly constrained shapes. + // In the Typescript server project, this is only done to generate constrained types for simple shapes (e.g. + // a `string` shape with the `length` trait), but these always remain `pub(crate)`. + .let { + if (includeConstrainedShapeProvider) ConstrainedShapeSymbolProvider(it, serviceShape, constrainedTypes) else it + } + // Generate different types for EventStream shapes (e.g. transcribe streaming) + .let { EventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it, CodegenTarget.SERVER) } + // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes + .let { BaseSymbolMetadataProvider(it, additionalAttributes = listOf()) } + // Constrained shapes generate newtypes that need the same derives we place on types generated from aggregate shapes. + .let { ConstrainedShapeSymbolMetadataProvider(it, constrainedTypes) } + // Streaming shapes need different derives (e.g. they cannot derive Eq) + .let { TsStreamingShapeMetadataProvider(it) } + // Derive `Eq` and `Hash` if possible. + .let { DeriveEqAndHashSymbolMetadataProvider(it) } + // Rename shapes that clash with Rust reserved words & and other SDK specific features e.g. `send()` cannot + // be the name of an operation input + .let { RustReservedWordSymbolProvider(it, ServerReservedWords) } + // Allows decorators to inject a custom symbol provider + .let { codegenDecorator.symbolProvider(it) } + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCargoDependency.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCargoDependency.kt new file mode 100644 index 0000000000..5c3b4c2209 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCargoDependency.kt @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.CratesIo +import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig + +/** + * Object used *exclusively* in the runtime of the Typescript server, for separation concerns. + * Analogous to the companion object in [CargoDependency] and [software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency]; see its documentation for details. + * For a dependency that is used in the client, or in both the client and the server, use [CargoDependency] directly. + */ +object TsServerCargoDependency { + val Napi: CargoDependency = CargoDependency("napi", CratesIo("2.11"), features = setOf("tokio_rt", "napi8")) + val NapiDerive: CargoDependency = CargoDependency("napi-derive", CratesIo("2.11")) + val NapiBuild: CargoDependency = CargoDependency("napi-build", CratesIo("2.0"), DependencyScope.Build) + val Tokio: CargoDependency = CargoDependency("tokio", CratesIo("1.20.1"), features = setOf("full")) + val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) + val Tower: CargoDependency = CargoDependency("tower", CratesIo("0.4")) + val TowerHttp: CargoDependency = CargoDependency("tower-http", CratesIo("0.3"), features = setOf("trace")) + val Hyper: CargoDependency = CargoDependency("hyper", CratesIo("0.14.12"), features = setOf("server", "http1", "http2", "tcp", "stream")) + val NumCpus: CargoDependency = CargoDependency("num_cpus", CratesIo("1.13")) + val ParkingLot: CargoDependency = CargoDependency("parking_lot", CratesIo("0.12")) + val Socket2: CargoDependency = CargoDependency("socket2", CratesIo("0.4")) + + fun smithyHttpServer(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-http-server") + fun smithyHttpServerTs(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-http-server-typescript") +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt new file mode 100644 index 0000000000..b1513b1be3 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt @@ -0,0 +1,213 @@ + +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.build.PluginContext +import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.NullableIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.ErrorTrait +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenVisitor +import software.amazon.smithy.rust.codegen.server.smithy.ServerModuleDocProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerModuleProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings +import software.amazon.smithy.rust.codegen.server.smithy.ServerSymbolProviders +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsApplicationGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsServerEnumGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsServerOperationErrorGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsServerOperationHandlerGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsServerStructureGenerator + +/** + * Entrypoint for Typescript server-side code generation. This class will walk the in-memory model and + * generate all the needed types by calling the accept() function on the available shapes. + * + * This class inherits from [ServerCodegenVisitor] since it uses most of the functionalities of the super class + * and have to override the symbol provider with [TsServerSymbolProvider]. + */ +class TsServerCodegenVisitor( + context: PluginContext, + private val codegenDecorator: ServerCodegenDecorator, +) : ServerCodegenVisitor(context, codegenDecorator) { + + init { + val symbolVisitorConfig = + RustSymbolProviderConfig( + runtimeConfig = settings.runtimeConfig, + renameExceptions = false, + nullabilityCheckMode = NullableIndex.CheckMode.SERVER, + moduleProvider = ServerModuleProvider, + ) + val baseModel = baselineTransform(context.model) + val service = settings.getService(baseModel) + val (protocol, generator) = + ServerProtocolLoader( + codegenDecorator.protocols( + service.id, + ServerProtocolLoader.DefaultProtocols, + ), + ) + .protocolFor(context.model, service) + protocolGeneratorFactory = generator + + model = codegenDecorator.transformModel(service, baseModel, settings) + + // `publicConstrainedTypes` must always be `false` for the Typescript server, since Typescript generates its own + // wrapper newtypes. + settings = settings.copy(codegenConfig = settings.codegenConfig.copy(publicConstrainedTypes = false)) + + fun baseSymbolProviderFactory( + settings: ServerRustSettings, + model: Model, + serviceShape: ServiceShape, + rustSymbolProviderConfig: RustSymbolProviderConfig, + publicConstrainedTypes: Boolean, + includeConstraintShapeProvider: Boolean, + codegenDecorator: ServerCodegenDecorator, + ) = RustServerCodegenTsPlugin.baseSymbolProvider(settings, model, serviceShape, rustSymbolProviderConfig, publicConstrainedTypes, includeConstraintShapeProvider, codegenDecorator) + + val serverSymbolProviders = ServerSymbolProviders.from( + settings, + model, + service, + symbolVisitorConfig, + settings.codegenConfig.publicConstrainedTypes, + codegenDecorator, + ::baseSymbolProviderFactory, + ) + + // Override `codegenContext` which carries the various symbol providers. + codegenContext = + ServerCodegenContext( + model, + serverSymbolProviders.symbolProvider, + null, + service, + protocol, + settings, + serverSymbolProviders.unconstrainedShapeSymbolProvider, + serverSymbolProviders.constrainedShapeSymbolProvider, + serverSymbolProviders.constraintViolationSymbolProvider, + serverSymbolProviders.pubCrateConstrainedShapeSymbolProvider, + ) + + codegenContext = codegenContext.copy( + moduleDocProvider = codegenDecorator.moduleDocumentationCustomization( + codegenContext, + TsServerModuleDocProvider(ServerModuleDocProvider(codegenContext)), + ), + ) + + // Override `rustCrate` which carries the symbolProvider. + rustCrate = RustCrate( + context.fileManifest, codegenContext.symbolProvider, settings.codegenConfig, + codegenContext.expectModuleDocProvider(), + ) + // Override `protocolGenerator` which carries the symbolProvider. + protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) + } + + /** + * Structure Shape Visitor + * + * For each structure shape, generate: + * - A Rust structure for the shape ([StructureGenerator]). + * - A builder for the shape. + * + * This function _does not_ generate any serializers. + */ + override fun structureShape(shape: StructureShape) { + logger.info("[js-server-codegen] Generating a structure $shape") + rustCrate.useShapeWriter(shape) { + // Use Typescript specific structure generator that adds the #[napi] attribute + // and implementation. + TsServerStructureGenerator(model, codegenContext.symbolProvider, this, shape).render() + + shape.getTrait()?.also { errorTrait -> + ErrorImplGenerator( + model, + codegenContext.symbolProvider, + this, + shape, + errorTrait, + codegenDecorator.errorImplCustomizations(codegenContext, emptyList()), + ).render(CodegenTarget.SERVER) + } + + renderStructureShapeBuilder(shape, this) + } + } + + /** + * String Shape Visitor + * + * Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. + */ + override fun stringShape(shape: StringShape) { + fun tsServerEnumGeneratorFactory(codegenContext: ServerCodegenContext, shape: StringShape) = + TsServerEnumGenerator(codegenContext, shape, validationExceptionConversionGenerator) + stringShape(shape, ::tsServerEnumGeneratorFactory) + } + + /** + * Union Shape Visitor + * + * Generate an `enum` for union shapes. + * + * Note: this does not generate serializers + */ + override fun unionShape(shape: UnionShape) { + throw CodegenException("Union shapes are not supported in Typescript yet") + } + + /** + * Generate service-specific code for the model: + * - Serializers + * - Deserializers + * - Trait implementations + * - Protocol tests + * - Operation structures + * - Typescript operation handlers + */ + override fun serviceShape(shape: ServiceShape) { + super.serviceShape(shape) + + logger.info("[ts-server-codegen] Generating a service $shape") + + val serverProtocol = protocolGeneratorFactory.protocol(codegenContext) as ServerProtocol + rustCrate.withModule(TsServerRustModule.TsServerApplication) { + TsApplicationGenerator(codegenContext, serverProtocol).render(this) + } + } + + override fun operationShape(shape: OperationShape) { + super.operationShape(shape) + rustCrate.withModule(TsServerRustModule.TsOperationAdapter) { + TsServerOperationHandlerGenerator(codegenContext, shape).render(this) + } + + rustCrate.withModule(ServerRustModule.Error) { + TsServerOperationErrorGenerator(codegenContext.model, codegenContext.symbolProvider, shape).render(this) + } + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerModuleDocProvider.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerModuleDocProvider.kt new file mode 100644 index 0000000000..48efe087d6 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerModuleDocProvider.kt @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.docs +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider + +object TsServerRustModule { + val TsOperationAdapter = RustModule.public("ts_operation_adaptor") + val TsServerApplication = RustModule.public("ts_server_application") +} + +class TsServerModuleDocProvider(private val base: ModuleDocProvider) : ModuleDocProvider { + override fun docsWriter(module: RustModule.LeafModule): Writable? { + val strDoc: (String) -> Writable = { str -> writable { docs(str) } } + return when (module) { + TsServerRustModule.TsServerApplication -> strDoc("Ts server and application implementation.") + // TODO(ServerTeam): Document this module (I don't have context) + TsServerRustModule.TsOperationAdapter -> null + else -> base.docsWriter(module) + } + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerRuntimeType.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerRuntimeType.kt new file mode 100644 index 0000000000..281563335f --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerRuntimeType.kt @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType + +/** + * Object used *exclusively* in the runtime of the Node server, for separation concerns. + * Analogous to the companion object in [RuntimeType] and [software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType]; see its documentation for details. + * For a runtime type that is used in the client, or in both the client and the server, use [RuntimeType] directly. + */ +object TsServerRuntimeType { + fun blob(runtimeConfig: RuntimeConfig) = + TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType().resolve("types::Blob") + + fun byteStream(runtimeConfig: RuntimeConfig) = + TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType().resolve("types::ByteStream") + + fun dateTime(runtimeConfig: RuntimeConfig) = + TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType().resolve("types::DateTime") + + fun document(runtimeConfig: RuntimeConfig) = + TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType().resolve("types::Document") +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerSymbolProvider.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerSymbolProvider.kt new file mode 100644 index 0000000000..a7ca74d064 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerSymbolProvider.kt @@ -0,0 +1,135 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.BlobShape +import software.amazon.smithy.model.shapes.DocumentShape +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.NumberShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.TimestampShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.core.smithy.SymbolMetadataProvider +import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor +import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.isStreaming +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings +import java.util.logging.Logger + +/** + * Symbol visitor allowing that recursively replace symbols in nested shapes. + * + * Input / output / error structures can refer to complex types like the ones implemented inside + * `aws_smithy_types` (a good example is `aws_smithy_types::Blob`). + * `aws_smithy_http_server_typescript::types` wraps those types that do not implement directly the + * `napi` trait and cannot be shared safely with Typescript, providing an idiomatic Typescript / Rust API. + * + * This symbol provider ensures types not implementing `pyo3::PyClass` are swapped with their wrappers from + * `aws_smithy_http_server_typescript::types`. + */ +class TsServerSymbolVisitor( + settings: ServerRustSettings, + model: Model, + serviceShape: ServiceShape?, + config: RustSymbolProviderConfig, +) : SymbolVisitor(settings, model, serviceShape, config) { + private val runtimeConfig = config.runtimeConfig + private val logger = Logger.getLogger(javaClass.name) + + override fun toSymbol(shape: Shape): Symbol { + val initial = shape.accept(this) + + if (shape !is MemberShape) { + return initial + } + val target = model.expectShape(shape.target) + val container = model.expectShape(shape.container) + + // We are only targeting non-synthetic inputs and outputs. + if (!container.hasTrait() && !container.hasTrait()) { + return initial + } + + // We are only targeting streaming blobs as the rest of the symbols do not change if streaming is enabled. + // For example a TimestampShape doesn't become a different symbol when streaming is involved, but BlobShape + // become a ByteStream. + return if (target is BlobShape && shape.isStreaming(model)) { + TsServerRuntimeType.byteStream(runtimeConfig).toSymbol() + } else { + initial + } + } + + override fun timestampShape(shape: TimestampShape?): Symbol { + return TsServerRuntimeType.dateTime(runtimeConfig).toSymbol() + } + + override fun blobShape(shape: BlobShape?): Symbol { + return TsServerRuntimeType.blob(runtimeConfig).toSymbol() + } + + override fun documentShape(shape: DocumentShape?): Symbol { + return TsServerRuntimeType.document(runtimeConfig).toSymbol() + } +} + +/** + * SymbolProvider to drop the PartialEq bounds in streaming shapes + * + * Streaming shapes equality cannot be checked without reading the body. Because of this, these shapes + * do not implement `PartialEq`. + * + * Note that since streaming members can only be used on the root shape, this can only impact input and output shapes. + */ +class TsStreamingShapeMetadataProvider(private val base: RustSymbolProvider) : SymbolMetadataProvider(base) { + override fun structureMeta(structureShape: StructureShape): RustMetadata { + val baseMetadata = base.toSymbol(structureShape).expectRustMetadata() + return if (structureShape.hasStreamingMember(model)) { + baseMetadata.withoutDerives(RuntimeType.PartialEq) + } else { + baseMetadata + } + } + + override fun unionMeta(unionShape: UnionShape): RustMetadata { + val baseMetadata = base.toSymbol(unionShape).expectRustMetadata() + return if (unionShape.hasStreamingMember(model)) { + baseMetadata.withoutDerives(RuntimeType.PartialEq) + } else { + baseMetadata + } + } + + override fun memberMeta(memberShape: MemberShape) = base.toSymbol(memberShape).expectRustMetadata() + override fun enumMeta(stringShape: StringShape): RustMetadata = + RustMetadata( + setOf(RuntimeType.Eq, RuntimeType.Ord, RuntimeType.PartialEq, RuntimeType.PartialOrd, RuntimeType.Debug), + listOf(), + Visibility.PUBLIC, + ) + + override fun listMeta(listShape: ListShape) = base.toSymbol(listShape).expectRustMetadata() + override fun mapMeta(mapShape: MapShape) = base.toSymbol(mapShape).expectRustMetadata() + override fun stringMeta(stringShape: StringShape) = base.toSymbol(stringShape).expectRustMetadata() + override fun numberMeta(numberShape: NumberShape) = base.toSymbol(numberShape).expectRustMetadata() + override fun blobMeta(blobShape: BlobShape) = base.toSymbol(blobShape).expectRustMetadata() +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/customizations/TsServerCodegenDecorator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/customizations/TsServerCodegenDecorator.kt new file mode 100644 index 0000000000..850af7b51a --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/customizations/TsServerCodegenDecorator.kt @@ -0,0 +1,114 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.customizations + +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.customizations.AddInternalServerErrorToAllOperationsDecorator +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * Configure the [lib] section of `Cargo.toml`. + * + * [lib] + * name = "$CRATE_NAME" + * crate-type = ["cdylib"] + */ +class CdylibManifestDecorator : ServerCodegenDecorator { + override val name: String = "CdylibDecorator" + override val order: Byte = 0 + + override fun crateManifestCustomizations( + codegenContext: ServerCodegenContext, + ): ManifestCustomizations = + mapOf( + "lib" to mapOf( + "crate-type" to listOf("cdylib"), + ), + ) +} + +class NapiBuildRsDecorator : ServerCodegenDecorator { + override val name: String = "NapiBuildRsDecorator" + override val order: Byte = 0 + private val napi_build = TsServerCargoDependency.NapiBuild.toType() + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + rustCrate.withFile("build.rs") { + rustTemplate( + """ + fn main() { + #{napi_build}::setup(); + } + """, + "napi_build" to napi_build, + ) + } + } +} + +class NapiPackageJsonDecorator : ServerCodegenDecorator { + override val name: String = "NapiPackageJsonDecorator" + override val order: Byte = 0 + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + val name = codegenContext.settings.moduleName.toSnakeCase() + val version = codegenContext.settings.moduleVersion + + // TODO(https://github.com/awslabs/smithy-rs/issues/2317): we should probably use a real JSON writer, but I did not want to add + // other external libraries at this stage. + rustCrate.withFile("package.json") { + val content = """{ + "name": "@amzn/$name", + "version": "$version", + "main": "index.js", + "types": "index.d.ts", + "napi": { + "name": "$name", + "triple": {} + }, + "devDependencies": { + "@napi-rs/cli": ">=2", + "@types/node": ">=18" + }, + "engines": { + "node": ">=18" + }, + "scripts": { + "artifacts": "napi artifacts", + "build": "napi build --platform --release", + "build:debug": "napi build --platform", + "prepublishOnly": "napi prepublish -t npm", + "universal": "napi universal", + "version": "napi version" + }, + "packageManager": "yarn", + "dependencies": { + "yarn": ">=1" + } +}""" + this.write(content) + } + } +} + +val DECORATORS = arrayOf( + /** + * Add the [InternalServerError] error to all operations. + * This is done because the Typescript interpreter can raise eceptions during execution. + */ + AddInternalServerErrorToAllOperationsDecorator(), + // Add the [lib] section to Cargo.toml to configure the generation of the shared library. + CdylibManifestDecorator(), + // Add the build.rs file needed to generate Typescript code. + NapiBuildRsDecorator(), + // Add the napi package.json. + NapiPackageJsonDecorator(), +) diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsApplicationGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsApplicationGenerator.kt new file mode 100644 index 0000000000..92e346fa91 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsApplicationGenerator.kt @@ -0,0 +1,314 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.knowledge.TopDownIndex +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.util.toPascalCase +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * Generates a Typescript compatible application and server that can be configured from Typescript. + */ +class TsApplicationGenerator( + codegenContext: CodegenContext, + private val protocol: ServerProtocol, +) { + private val index = TopDownIndex.of(codegenContext.model) + private val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet( + compareBy { + it.id + }, + ).toList() + private val symbolProvider = codegenContext.symbolProvider + private val libName = codegenContext.settings.moduleName.toSnakeCase() + private val runtimeConfig = codegenContext.runtimeConfig + private val service = codegenContext.serviceShape + private val serviceName = service.id.name.toPascalCase() + private val model = codegenContext.model + private val codegenScope = + arrayOf( + "SmithyServer" to ServerCargoDependency.smithyHttpServer(runtimeConfig).toType(), + "napi" to TsServerCargoDependency.Napi.toType(), + "napi_derive" to TsServerCargoDependency.NapiDerive.toType(), + "tokio" to TsServerCargoDependency.Tokio.toType(), + "tracing" to TsServerCargoDependency.Tracing.toType(), + "tower" to TsServerCargoDependency.Tower.toType(), + "tower_http" to TsServerCargoDependency.TowerHttp.toType(), + "num_cpus" to TsServerCargoDependency.NumCpus.toType(), + "hyper" to TsServerCargoDependency.Hyper.toType(), + "HashMap" to RuntimeType.HashMap, + "parking_lot" to TsServerCargoDependency.ParkingLot.toType(), + "http" to RuntimeType.Http, + "socket2" to TsServerCargoDependency.Socket2.toType(), + ) + + fun render(writer: RustWriter) { + writer.write("use napi_derive::napi;") + renderHandlers(writer) + renderApp(writer) + + // TODO(https://github.com/napi-rs/napi-rs/issues/1377) Move these to be part of the runtime crate. + renderSocket(writer) + renderServer(writer) + } + + fun renderHandlers(writer: RustWriter) { + Attribute(derive(RuntimeType.Clone)).render(writer) + writer.rustBlock("""pub struct Handlers""") { + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name + val input = "crate::input::${operationName}Input" + val fnName = operationName.toSnakeCase() + rustTemplate( + """ + pub(crate) $fnName: #{napi}::threadsafe_function::ThreadsafeFunction< + $input, + #{napi}::threadsafe_function::ErrorStrategy::Fatal + >, + """, + *codegenScope, + ) + } + } + Attribute("""napi(object)""").render(writer) + writer.rustBlock("pub struct TsHandlers") { + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name + val input = "${operationName}Input" + val output = "${operationName}Output" + val fnName = operationName.toSnakeCase() + rustTemplate( + """ + ##[napi(ts_type = "(input: $input) => Promise<$output>")] + pub $fnName: #{napi}::JsFunction, + """, + *codegenScope, + ) + } + } + } + + private fun renderApp(writer: RustWriter) { + Attribute("napi").render(writer) + writer.rust( + """ + pub struct App { + handlers: Handlers, + } + """, + ) + Attribute("napi").render(writer) + writer.rustBlock("impl App") { + renderAppCreate(writer) + renderAppStart(writer) + } + } + + private fun renderAppCreate(writer: RustWriter) { + Attribute("napi(constructor)").render(writer) + writer.rustBlockTemplate( + """pub fn create(ts_handlers: TsHandlers) -> #{napi}::Result""", + *codegenScope, + ) { + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name + val input = "crate::input::${operationName}Input" + val fnName = operationName.toSnakeCase() + rustTemplate( + """ + let $fnName: #{napi}::threadsafe_function::ThreadsafeFunction< + $input, #{napi}::threadsafe_function::ErrorStrategy::Fatal + > = ts_handlers.$fnName.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?; + """, + *codegenScope, + ) + } + rust("let handlers = Handlers {") + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name.toSnakeCase() + rust(" $operationName: $operationName.clone(),") + } + rust("};") + writer.rust("Ok(Self{ handlers })") + } + } + + private fun renderAppStart(writer: RustWriter) { + Attribute("napi").render(writer) + writer.rustBlockTemplate( + """pub fn start(&self, socket: &TsSocket) -> #{napi}::Result<()>""", + *codegenScope, + ) { + rustTemplate( + """ + let plugins = #{SmithyServer}::plugin::PluginPipeline::new(); + let builder = crate::service::$serviceName::builder_with_plugins(plugins); + """, + *codegenScope, + ) + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name.toSnakeCase() + rust("let builder = builder.$operationName(crate::ts_operation_adaptor::$operationName);") + } + rustTemplate( + """ + let app = builder.build().expect("failed to build instance of $serviceName") + .layer(&#{SmithyServer}::AddExtensionLayer::new(self.handlers.clone())); + let service = #{tower}::util::BoxCloneService::new(app); + start_hyper_worker(socket, service).expect("failed to start the hyper server"); + Ok(()) + """, + *codegenScope, + ) + } + } + + private fun renderSocket(writer: RustWriter) { + Attribute("napi").render(writer) + Attribute(derive(RuntimeType.Debug)).render(writer) + writer.rustTemplate( + """ + pub struct TsSocket(#{socket2}::Socket); + """, + *codegenScope, + ) + + Attribute("napi").render(writer) + writer.rustBlock("impl TsSocket") { + writer.rust( + """ + /// Create a new UNIX `Socket` from an address, port and backlog. + /// If not specified, the backlog defaults to 1024 connections. + """.trimIndent(), + ) + Attribute("napi(constructor)").render(writer) + writer.rustBlockTemplate( + """pub fn new(address: String, port: i32, backlog: Option) -> #{napi}::Result""".trimIndent(), + *codegenScope, + ) { + writer.rustTemplate( + """ + let socket = Self::new_socket(address, port, backlog) + .map_err(|e| #{napi}::Error::from_reason(e.to_string()))?; + Ok(Self(socket)) + """, + *codegenScope, + ) + } + writer.rustBlockTemplate( + """pub fn new_socket(address: String, port: i32, backlog: Option) -> Result<#{socket2}::Socket, Box> """.trimIndent(), + *codegenScope, + ) { + writer.rustTemplate( + """ + let address: std::net::SocketAddr = format!("{}:{}", address, port).parse()?; + let domain = if address.is_ipv6() { + #{socket2}::Domain::IPV6 + } else { + #{socket2}::Domain::IPV4 + }; + let socket = #{socket2}::Socket::new(domain, #{socket2}::Type::STREAM, Some(#{socket2}::Protocol::TCP))?; + // Set value for the `SO_REUSEPORT` and `SO_REUSEADDR` options on this socket. + // This indicates that further calls to `bind` may allow reuse of local + // addresses. For IPv4 sockets this means that a socket may bind even when + // there's a socket already listening on this port. + socket.set_reuse_port(true)?; + socket.set_reuse_address(true)?; + socket.bind(&address.into())?; + socket.listen(backlog.unwrap_or(1024))?; + Ok(socket) + """.trimIndent(), + *codegenScope, + ) + } + + writer.rust( + """ + /// Clone the inner socket allowing it to be shared between multiple + /// Nodejs processes. + """.trimIndent(), + ) + Attribute("napi").render(writer) + writer.rustBlockTemplate( + """pub fn try_clone(&self) -> #{napi}::Result""".trimIndent(), + *codegenScope, + ) { + writer.rustTemplate( + """ + Ok(TsSocket( + self.0 + .try_clone() + .map_err(|e| #{napi}::Error::from_reason(e.to_string()))?, + )) + """.trimIndent(), + *codegenScope, + ) + } + } + + writer.rustBlock("impl TsSocket") { + writer.rustTemplate( + """pub fn to_raw_socket(&self) -> #{napi}::Result<#{socket2}::Socket> { + self.0 + .try_clone() + .map_err(|e| #{napi}::Error::from_reason(e.to_string())) + } + + """.trimIndent(), + *codegenScope, + ) + } + } + + private fun renderServer(writer: RustWriter) { + writer.rustBlockTemplate( + """pub fn start_hyper_worker( + socket: &TsSocket, + app: #{tower}::util::BoxCloneService< + #{http}::Request<#{SmithyServer}::body::Body>, + #{http}::Response<#{SmithyServer}::body::BoxBody>, + std::convert::Infallible, + >, + ) -> #{napi}::Result<()> + """.trimIndent(), + *codegenScope, + ) { + writer.rustTemplate( + """ + let server = #{hyper}::Server::from_tcp( + socket + .to_raw_socket()? + .try_into() + .expect("Unable to convert socket2::Socket into std::net::TcpListener"), + ) + .expect("Unable to create hyper server from shared socket") + .serve(#{SmithyServer}::routing::IntoMakeService::new(app)); + + let handle = #{tokio}::runtime::Handle::current(); + handle.spawn(async move { + // Process each socket concurrently. + server.await + }); + + Ok(()) + """.trimIndent(), + *codegenScope, + ) + } + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerEnumGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerEnumGenerator.kt new file mode 100644 index 0000000000..f12ebb5ef6 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerEnumGenerator.kt @@ -0,0 +1,52 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedEnum +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * To share enums defined in Rust with Typescript, `napi-rs` provides the `napi` trait. + * This class generates enums definitions and implements the `napi` trait. + */ +class TsConstrainedEnum( + codegenContext: ServerCodegenContext, + shape: StringShape, + validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) : ConstrainedEnum(codegenContext, shape, validationExceptionConversionGenerator) { + private val napiDerive = TsServerCargoDependency.NapiDerive.toType() + + override fun additionalEnumImpls(context: EnumGeneratorContext): Writable = writable { + this.rust("use napi::bindgen_prelude::ToNapiValue;") + } + + override fun additionalEnumAttributes(context: EnumGeneratorContext): List = + listOf(Attribute(napiDerive.resolve("napi"))) +} + +class TsServerEnumGenerator( + codegenContext: ServerCodegenContext, + shape: StringShape, + validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) : EnumGenerator( + codegenContext.model, + codegenContext.symbolProvider, + shape, + TsConstrainedEnum( + codegenContext, + shape, + validationExceptionConversionGenerator, + ), +) diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationErrorGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationErrorGenerator.kt new file mode 100644 index 0000000000..6384c5c07c --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationErrorGenerator.kt @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.OperationIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * Generates a unified error enum for [operation] and adds the Rust implementation for `napi` error. + */ +class TsServerOperationErrorGenerator( + private val model: Model, + private val symbolProvider: RustSymbolProvider, + private val operation: OperationShape, +) { + private val operationIndex = OperationIndex.of(model) + private val errors = operationIndex.getErrors(operation) + + fun render(writer: RustWriter) { + renderFromTsErr(writer) + } + + // TODO(https://github.com/awslabs/smithy-rs/issues/2317): match the Ts error type and return the right one. + private fun renderFromTsErr(writer: RustWriter) { + writer.rustTemplate( + """ + impl #{From}<#{napi}::Error> for #{Error} { + fn from(variant: #{napi}::Error) -> #{Error} { + crate::error::InternalServerError { message: variant.to_string() }.into() + } + } + + """, + "napi" to TsServerCargoDependency.Napi.toType(), + "Error" to symbolProvider.symbolForOperationError(operation), + "From" to RuntimeType.From, + ) + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationHandlerGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationHandlerGenerator.kt new file mode 100644 index 0000000000..787a0b7cc0 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationHandlerGenerator.kt @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * The Rust code responsible to run the Typescript business logic is implemented in this class,. + * + * We codegenerate all operations handlers (steps usually left to the developer in a pure + * Rust application), which are built into a `Router` by [TsServerApplicationGenerator]. + */ +class TsServerOperationHandlerGenerator( + codegenContext: CodegenContext, + private val operation: OperationShape, +) { + private val symbolProvider = codegenContext.symbolProvider + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope = + arrayOf( + "SmithyTs" to TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType(), + "SmithyServer" to TsServerCargoDependency.smithyHttpServer(runtimeConfig).toType(), + "napi" to TsServerCargoDependency.Napi.toType(), + ) + + fun render(writer: RustWriter) { + renderTsOperationHandlerImpl(writer) + } + + private fun renderTsOperationHandlerImpl(writer: RustWriter) { + val operationName = symbolProvider.toSymbol(operation).name + val input = "crate::input::${operationName}Input" + val output = "crate::output::${operationName}Output" + val error = "crate::error::${operationName}Error" + val fnName = operationName.toSnakeCase() + + writer.rustTemplate( + """ + /// Typescript handler for operation `$operationName`. + pub(crate) async fn $fnName( + input: $input, + handlers: #{SmithyServer}::Extension, + ) -> std::result::Result<$output, $error> { + handlers.$fnName.call_async::<#{napi}::bindgen_prelude::Promise<$output>>(input).await?.await.map_err(|e| e.into()) + } + """, + *codegenScope, + ) + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerStructureGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerStructureGenerator.kt new file mode 100644 index 0000000000..87d8300ca7 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerStructureGenerator.kt @@ -0,0 +1,50 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.ErrorTrait +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustInlineTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * To share structures defined in Rust with Typescript, `napi-rs` provides the `napi` trait. + * This class generates input / output / error structures definitions and implements the + * `napi` trait. + */ +class TsServerStructureGenerator( + model: Model, + private val symbolProvider: RustSymbolProvider, + private val writer: RustWriter, + private val shape: StructureShape, +) : StructureGenerator(model, symbolProvider, writer, shape, listOf()) { + + private val napiDerive = TsServerCargoDependency.NapiDerive.toType() + + override fun renderStructure() { + val flavour = if (shape.hasTrait()) { + "constructor" + } else { + "object" + } + Attribute( + writable { + rustInlineTemplate( + "#{napi}($flavour)", + "napi" to napiDerive.resolve("napi"), + ) + }, + ).render(writer) + super.renderStructure() + } +} diff --git a/codegen-server/typescript/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin b/codegen-server/typescript/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin new file mode 100644 index 0000000000..ee61dbc10a --- /dev/null +++ b/codegen-server/typescript/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin @@ -0,0 +1,5 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# +software.amazon.smithy.rust.codegen.server.typescript.smithy.RustServerCodegenTsPlugin diff --git a/design/book.toml b/design/book.toml index f8cfdd021f..3c6fffdaf5 100644 --- a/design/book.toml +++ b/design/book.toml @@ -1,9 +1,12 @@ [book] +title = "Smithy Rust" authors = ["Russell Cohen", "aws-sdk-rust@amazon.com"] language = "en" multilingual = false src = "src" -title = "AWS Rust SDK Design" + +[rust] +edition = "2021" [preprocessor.mermaid] command = "mdbook-mermaid" diff --git a/design/src/SUMMARY.md b/design/src/SUMMARY.md index ae41ea5246..34933fca19 100644 --- a/design/src/SUMMARY.md +++ b/design/src/SUMMARY.md @@ -6,6 +6,7 @@ - [Transport](transport/overview.md) - [HTTP Operations](transport/operation.md) - [HTTP Middleware](transport/middleware.md) + - [TLS Connector](transport/connector.md) - [Smithy](./smithy/overview.md) - [Simple Shapes](./smithy/simple_shapes.md) @@ -14,13 +15,16 @@ - [Endpoint Resolution](smithy/endpoint.md) - [Backwards Compatibility](smithy/backwards-compat.md) +- [Client](./client/overview.md) + - [What is the 'orchestrator' and why does it exist?](./client/orchestrator.md) + - [Identity and Auth](./client/identity_and_auth.md) + - [Server](./server/overview.md) - [Middleware](./server/middleware.md) - [Instrumentation](./server/instrumentation.md) - [Accessing Un-modelled Data](./server/from_parts.md) - [The Anatomy of a Service](./server/anatomy.md) - [Generating Common Service Code](./server/code_generation.md) - - [Generating the Pokémon Service](./server/pokemon_service.md) - [RFCs](./rfcs/overview.md) - [RFC-0001: Sharing configuration between multiple clients](./rfcs/rfc0001_shared_config.md) diff --git a/design/src/client/identity_and_auth.md b/design/src/client/identity_and_auth.md new file mode 100644 index 0000000000..d58efa1932 --- /dev/null +++ b/design/src/client/identity_and_auth.md @@ -0,0 +1,190 @@ +Identity and Auth in Clients +============================ + +The [Smithy specification establishes several auth related modeling traits] that can be applied to +operation and service shapes. To briefly summarize: + +- The auth schemes that are supported by a service are declared on the service shape +- Operation shapes MAY specify the subset of service-defined auth schemes they support. If none are specified, then all service-defined auth schemes are supported. + +A smithy code generator MUST support at least one auth scheme for every modeled operation, but it need not support ALL modeled auth schemes. + +This design document establishes how smithy-rs implements this specification. + +Terminology +----------- + +- **Auth:** Either a shorthand that represents both of the authentication and authorization terms below, +or an ambiguous representation of one of them. In this doc, this term will always refer to both. +- **Authentication:** The process of proving an entity is who they claim they are, sometimes referred to as AuthN. +- **Authorization:** The process of granting an authenticated entity the permission to +do something, sometimes referred to as AuthZ. +- **Identity:** The information required for authentication. +- **Signing:** The process of attaching metadata to a request that allows a server to authenticate that request. + +Overview of Smithy Client Auth +------------------------------ + +There are two stages to identity and auth: +1. Configuration +2. Execution + +### The configuration stage + +First, let's establish the aspects of auth that can be configured from the model at codegen time. + +- **Data** + - **AuthSchemeOptionResolverParams:** parameters required to resolve auth scheme options. These parameters are allowed + to come from both the client config and the operation input structs. + - **AuthSchemes:** a list of auth schemes that can be used to sign HTTP requests. This information + comes directly from the service model. + - **AuthSchemeProperties:** configuration from the auth scheme for the signer. + - **IdentityResolvers:** list of available identity resolvers. +- **Implementations** + - **IdentityResolver:** resolves an identity for use in authentication. + There can be multiple identity resolvers that need to be selected from. + - **Signer:** a signing implementation that signs a HTTP request. + - **AuthSchemeOptionResolver:** resolves a list of auth scheme options for a given operation and its inputs. + +As it is undocumented (at time of writing), this document assumes that the code generator +creates one service-level runtime plugin, and an operation-level runtime plugin per operation, hence +referred to as the service runtime plugin and operation runtime plugin. + +The code generator emits code to add identity resolvers and HTTP auth schemes to the config bag +in the service runtime plugin. It then emits code to register an interceptor in the operation runtime +plugin that reads the operation input to generate the auth scheme option resolver params (which also get added +to the config bag). + +### The execution stage + +At a high-level, the process of resolving an identity and signing a request looks as follows: + +1. Retrieve the `AuthSchemeOptionResolverParams` from the config bag. The `AuthSchemeOptionResolverParams` allow client +config and operation inputs to play a role in which auth scheme option is selected. +2. Retrieve the `AuthSchemeOptionResolver` from the config bag, and use it to resolve the auth scheme options available +with the `AuthSchemeOptionResolverParams`. The returned auth scheme options are in priority order. +3. Retrieve the `IdentityResolvers` list from the config bag. +4. For each auth scheme option: + 1. Attempt to find an HTTP auth scheme for that auth scheme option in the config bag (from the `AuthSchemes` list). + 2. If an auth scheme is found: + 1. Use the auth scheme to extract the correct identity resolver from the `IdentityResolvers` list. + 2. Retrieve the `Signer` implementation from the auth scheme. + 3. Use the `IdentityResolver` to resolve the identity needed for signing. + 4. Sign the request with the identity, and break out of the loop from step #4. + +In general, it is assumed that if an HTTP auth scheme exists for an auth scheme option, then an identity resolver +also exists for that auth scheme option. Otherwise, the auth option was configured incorrectly during codegen. + +How this looks in Rust +---------------------- + +The client will use trait objects and dynamic dispatch for the `IdentityResolver`, +`Signer`, and `AuthSchemeOptionResolver` implementations. Generics could potentially be used, +but the number of generic arguments and trait bounds in the orchestrator would balloon to +unmaintainable levels if each configurable implementation in it was made generic. + +These traits look like this: + +```rust,ignore +#[derive(Clone, Debug)] +pub struct AuthSchemeId { + scheme_id: &'static str, +} + +pub trait AuthSchemeOptionResolver: Send + Sync + Debug { + fn resolve_auth_scheme_options<'a>( + &'a self, + params: &AuthSchemeOptionResolverParams, + ) -> Result, BoxError>; +} + +pub trait IdentityResolver: Send + Sync + Debug { + fn resolve_identity(&self, config: &ConfigBag) -> BoxFallibleFut; +} + +pub trait Signer: Send + Sync + Debug { + /// Return a signed version of the given request using the given identity. + /// + /// If the provided identity is incompatible with this signer, an error must be returned. + fn sign_http_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + runtime_components: &RuntimeComponents, + config_bag: &ConfigBag, + ) -> Result<(), BoxError>; +} +``` + +`IdentityResolver` and `Signer` implementations are both given an `Identity`, but +will need to understand what the concrete data type underlying that identity is. The `Identity` struct +uses a `Arc` to represent the actual identity data so that generics are not needed in +the traits: + +```rust,ignore +#[derive(Clone, Debug)] +pub struct Identity { + data: Arc, + expiration: Option, +} +``` + +Identities can often be cached and reused across several requests, which is why the `Identity` uses `Arc` +rather than `Box`. This also reduces the allocations required. The signer implementations +will use downcasting to access the identity data types they understand. For example, with AWS SigV4, +it might look like the following: + +```rust,ignore +fn sign_http_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + runtime_components: &RuntimeComponents, + config_bag: &ConfigBag, +) -> Result<(), BoxError> { + let aws_credentials = identity.data::() + .ok_or_else(|| "The SigV4 signer requires AWS credentials")?; + let access_key = &aws_credentials.secret_access_key; + // -- snip -- +} +``` + +Also note that identity data structs are expected to censor their own sensitive fields, as +`Identity` implements the automatically derived `Debug` trait. + +### Challenges with this `Identity` design + +A keen observer would note that there is an `expiration` field on `Identity`, and may ask, "what about +non-expiring identities?" This is the result of a limitation on `Box`, where it can only be +downcasted to concrete types. There is no way to downcast to a `dyn Trait` since the information required +to verify that that type is that trait is lost at compile time (a `std::any::TypeId` only encodes information +about the concrete type). + +In an ideal world, it would be possible to extract the expiration like this: +```rust,ignore +pub trait ExpiringIdentity { + fn expiration(&self) -> SystemTime; +} + +let identity: Identity = some_identity(); +if let Some(expiration) = identity.data::<&dyn ExpiringIdentity>().map(ExpiringIdentity::expiration) { + // make a decision based on that expiration +} +``` + +Theoretically, you should be able to save off additional type information alongside the `Box` and use +unsafe code to transmute to known traits, but it is difficult to implement in practice, and adds unsafe code +in a security critical piece of code that could otherwise be avoided. + +The `expiration` field is a special case that is allowed onto the `Identity` struct directly since identity +cache implementations will always need to be aware of this piece of information, and having it as an `Option` +still allows for non-expiring identities. + +Ultimately, this design constrains `Signer` implementations to concrete types. There is no world +where an `Signer` can operate across multiple unknown identity data types via trait, and that +should be OK since the signer implementation can always be wrapped with an implementation that is aware +of the concrete type provided by the identity resolver, and can do any necessary conversions. + +[Smithy specification establishes several auth related modeling traits]: https://smithy.io/2.0/spec/authentication-traits.html diff --git a/design/src/client/orchestrator.md b/design/src/client/orchestrator.md new file mode 100644 index 0000000000..d0330905bd --- /dev/null +++ b/design/src/client/orchestrator.md @@ -0,0 +1,211 @@ +## What is the orchestrator? + +At a very high level, an orchestrator is a process for transforming requests into responses. Please enjoy this fancy chart: + +```mermaid +flowchart TB + A(Orchestrate)-->|Input|B(Request serialization) + B-->|Transmit Request|C(Connection) + C-->|Transmit Response|D(Response deserialization) + D-->|Success|E("Ok(Output)") + D-->|Unretryable Failure|F("Err(SdkError)") + D-->|Retryable Failure|C +``` + +This process is also referred to as the "request/response lifecycle." In this example, the types of "transmit request" and "transmit response" are protocol-dependent. Typical operations use [HTTP], but we plan to support other protocols like [MQTT] in the future. + +In addition to the above steps, the orchestrator must also handle: + +- **Endpoint resolution:** figuring out which URL to send a request to. +- **Authentication, identity resolution, and request signing:** Figuring out who is sending the request, their credentials, and how we should insert the credentials into a request. +- **Interceptors**: Running lifecycle hooks at each point in the request/response lifecycle. +- **Runtime Plugins:** Resolving configuration from config builders. +- **Retries:** Categorizing responses from services and deciding whether to retry and how long to wait before doing so. +- **Trace Probes:** A [sink] for events that occur during the request/response lifecycle. + +## How is an orchestrator configured? + +While the structure of an orchestrator is fixed, the actions it takes during its lifecycle are highly configurable. Users have two ways to configure this process: + +- **Runtime Plugins**: + - **When can these be set?** Any time before calling `orchestrate`. + - **When are they called by the orchestrator?** In two batches, at the very beginning of `orchestrate`. + - **What can they do?** + - They can set configuration to be used by the orchestrator or in interceptors. + - They can set interceptors. + - **Are they user-definable?** No. At present, only smithy-rs maintainers may define these. +- **Interceptors**: + - **When can these be set?** Any time before calling `orchestrate`. + - **When are they called by the orchestrator?** At each step in the request-response lifecycle. + - **What can they do?** + - They can set configuration to be used by the orchestrator or in interceptors. + - They can log information. + - Depending on when they're run, they can modify the input, transmit request, transmit response, and the output/error. + - **Are they user-definable?** Yes. + +Configuration for a request is constructed by runtime plugins just after calling `orchestrate`. Configuration is stored in a `ConfigBag`: a hash map that's keyed on type's [`TypeId`][TypeId] (an opaque object, managed by the Rust compiler, which references some type.) + +## What does the orchestrator do? + +The orchestrator's work is divided into four phases: + +*NOTE: If an interceptor fails, then the other interceptors for that lifecycle event are still run. All resulting errors are collected and emitted together.* + +0. **Building the `ConfigBag` and mounting interceptors**. + - *This phase is fallible.* + - An interceptor context is created. This will hold request and response objects, making them available to interceptors. + - All runtime plugins set at the client-level are run. These plugins can set config and mount interceptors. Any _"read before execution"_ interceptors that have been set get run. + - All runtime plugins set at the operation-level are run. These plugins can also set config and mount interceptors. Any new _"read before execution"_ interceptors that have been set get run. +1. **Request Construction** + - *This phase is fallible.* + - The _"read before serialization"_ and _"modify before serialization"_ interceptors are called. + - The input is serialized into a transmit request. + - The _"read after serialization"_ and _"modify before retry_ loop" interceptors are called. + - Before making an attempt, the retry handler is called to check if an attempt should be made. The retry handler makes this decision for an initial attempt as well as for the retry attempts. If an initial attempt should be made, then the orchestrator enters the Dispatch phase. Otherwise, a throttling error is returned. +2. **Request Dispatch** + - *This phase is fallible. This phase's tasks are performed in a loop. Retryable request failures will be retried, and unretryable failures will end the loop.* + - The _"read before attempt"_ interceptors are run. + - An endpoint is resolved according to an endpoint resolver. The resolved endpoint is then applied to the transmit request. + - The _"read before signing"_ and _"modify before signing"_ interceptors are run. + - An identity and a signer are resolved according to an authentication resolver. The signer then signs the transmit request with the identity. + - The _"read after signing"_, _"read before transmit"_, and _"modify before transmit"_ interceptors are run. + - The transmit request is passed into the connection, and a transmit response is received. + - The _"read after transmit"_, _"read before deserialization"_, and _"modify before deserialization"_ interceptors are run. + - The transmit response is deserialized. + - The _"read after attempt"_ and _"modify before attempt_ completion" interceptors are run. + - The retry strategy is called to check if a retry is necessary. If a retry is required, the Dispatch phase restarts. Otherwise, the orchestrator enters the Response Handling phase. +3. **Response Handling** + - *This phase is fallible.* + - The _"read after deserialization"_ and _"modify before completion"_ interceptors are run. + - Events are dispatched to any trace probes that the user has set. + - The _"read after execution"_ interceptors are run. + +At the end of all this, the response is returned. If an error occurred at any point, then the response will contain one or more errors, depending on what failed. Otherwise, the output will be returned. + +## How is the orchestrator implemented in Rust? + +### Avoiding generics at all costs + +In designing the orchestrator, we sought to solve the problems we had with the original smithy client. The client made heavy use of generics, allowing for increased performance, but at the cost of increased maintenance burden and [increased compile times][monomorphization]. The Rust compiler, usually very helpful, isn't well-equipped to explain trait errors when bounds are this complex, and so the resulting client was difficult to extend. [Trait aliases] would have helped, but they're not (at the time of writing) available. + +*The type signatures for the old client and its `call` method:* + +```rust,ignore +impl Client +where + C: bounds::SmithyConnector, + M: bounds::SmithyMiddleware, + R: retry::NewRequestPolicy, +{ + pub async fn call(&self, op: Operation) -> Result> + where + O: Send + Sync, + E: std::error::Error + Send + Sync + 'static, + Retry: Send + Sync, + R::Policy: bounds::SmithyRetryPolicy, + Retry: ClassifyRetry, SdkError>, + bounds::Parsed<>::Service, O, Retry>: + Service, Response=SdkSuccess, Error=SdkError> + Clone, + { + self.call_raw(op).await.map(|res| res.parsed) + } + + pub async fn call_raw( + &self, + op: Operation, + ) -> Result, SdkError> + where + O: Send + Sync, + E: std::error::Error + Send + Sync + 'static, + Retry: Send + Sync, + R::Policy: bounds::SmithyRetryPolicy, + Retry: ClassifyRetry, SdkError>, + // This bound is not _technically_ inferred by all the previous bounds, but in practice it + // is because _we_ know that there is only implementation of Service for Parsed + // (ParsedResponseService), and it will apply as long as the bounds on C, M, and R hold, + // and will produce (as expected) Response = SdkSuccess, Error = SdkError. But Rust + // doesn't know that -- there _could_ theoretically be other implementations of Service for + // Parsed that don't return those same types. So, we must give the bound. + bounds::Parsed<>::Service, O, Retry>: + Service, Response=SdkSuccess, Error=SdkError> + Clone, + { + // The request/response lifecycle + } +} +``` + +*The type signature for the new `orchestrate` method:* + +```rust,ignore +pub async fn orchestrate( + input: Input, + runtime_plugins: &RuntimePlugins, + // Currently, SdkError is HTTP-only. We currently use it for backwards-compatibility purposes. + // The `HttpResponse` generic will likely be removed in the future. +) -> Result> { + // The request/response lifecycle +} +``` + +Wait a second, I hear you ask. "I see an `Input` and `Output` there, but you're not declaring any generic type arguments. What gives?" + +I'm glad you asked. Generally, when you need traits, but you aren't willing to use generic type arguments, then you must [`Box`][std::boxed]. Polymorphism is achieved through [dynamic dispatch] instead of [static dispatch], and this comes with a small runtime cost. + +So, what are `Input` and `Output`? They're our own special flavor of a boxed trait object. + +```rust,ignore +pub type Input = TypeErasedBox; +pub type Output = TypeErasedBox; +pub type Error = TypeErasedBox; + +/// A new-type around `Box` +#[derive(Debug)] +pub struct TypeErasedBox { + inner: Box, +} +``` + +The orchestrator itself doesn't know about any concrete types. Instead, it passes boxed data between the various components of the request/response lifecycle. Individual components access data in two ways: + +1. **From the `ConfigBag`:** + - *(with an accessor)* `let retry_strategy = cfg.retry_strategy();` + - *(with the get method)* `let retry_strategy = cfg.get::>()` +2. **From the `InterceptorContext`:** + - *(owned)* `let put_object_input: PutObjectInput = ctx.take_input().unwrap().downcast().unwrap()?;` + - *(by reference)* `let put_object_input = ctx.input().unwrap().downcast_ref::().unwrap();` + +Users can only call `ConfigBag::get` or [downcast] a `TypeErasedBox` to types they have access to, which allows maintainers to ensure encapsulation. For example: a plugin writer may declare a private type, place it in the config bag, and then later retrieve it. Because the type is private, only code in the same crate/module can ever insert or retrieve it. Therefore, there's less worry that someone will depend on a hidden, internal detail and no worry they'll accidentally overwrite a type in the bag. + +> *NOTE: When inserting values into a config bag, using one of the `set_` methods is always preferred, as this prevents mistakes related to inserting similar, but incorrect types.* + +### The actual code + +The current implementation of `orchestrate` is defined [here][orchestrate-impl], in the [`aws-smithy-runtime` crate][aws-smithy-runtime]. Related code can be found in the [`aws-smithy-runtime-api` crate][aws-smithy-runtime]. + +## Frequently asked questions + +### Why can't users create and use their own runtime plugins? + +We chose to hide the runtime plugin API from users because we are concerned that exposing it will cause more problems than it solves. Instead, we encourage users to use interceptors. This is because, when setting a runtime plugin, any existing runtime plugin with the same type will be replaced. For example, there can only be one retry strategy or response deserializer. Errors resulting from unintentionally overriding a plugin would be difficult for users to diagnose, and would consume valuable development time. + +### Why does the orchestrator exist? + +The orchestrator exists because there is an AWS-internal initiative to bring the architecture of all AWS SDKs closer to one another. + +### Why does this document exist when there's already an orchestrator RFC? + +Because RFCs become outdated as designs evolve. It is our intention to keep this document up to date with our current implementation. + +[HTTP]: https://en.wikipedia.org/wiki/HTTP +[MQTT]: https://en.wikipedia.org/wiki/MQTT +[sink]: https://en.wikipedia.org/wiki/Sink_(computing) +[trait aliases]: https://github.com/rust-lang/rust/issues/41517 +[std::boxed]: https://doc.rust-lang.org/std/boxed/index.html +[monomorphization]: https://rustc-dev-guide.rust-lang.org/backend/monomorph.html#polymorphization +[static dispatch]: https://doc.rust-lang.org/book/ch10-01-syntax.html#performance-of-code-using-generics +[dynamic dispatch]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[TypeId]: https://doc.rust-lang.org/stable/std/any/struct.TypeId.html +[downcast]: https://en.wikipedia.org/wiki/Downcasting +[orchestrate-impl]: https://github.com/awslabs/smithy-rs/blob/8bc93fc04dd8c8d7447bfe3f5196a75cba0b10ba/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs#L23 +[aws-smithy-runtime]: https://github.com/awslabs/smithy-rs/tree/main/rust-runtime/aws-smithy-runtime +[aws-smithy-runtime-api]: https://github.com/awslabs/smithy-rs/tree/main/rust-runtime/aws-smithy-runtime-api diff --git a/design/src/client/overview.md b/design/src/client/overview.md new file mode 100644 index 0000000000..4d4f31d7e0 --- /dev/null +++ b/design/src/client/overview.md @@ -0,0 +1,7 @@ +# Smithy Client + +`smithy-rs` provides the ability to generate a client whose operations defined by a smithy model. The documents referenced +here explain aspects of the client in greater detail. + +- [What is the 'orchestrator' and why does it exist?](./orchestrator.md) +- [Identity and Auth](./identity_and_auth.md) diff --git a/design/src/contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md b/design/src/contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md index 8a15f29f4c..3e9fa6a767 100644 --- a/design/src/contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md +++ b/design/src/contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md @@ -25,7 +25,7 @@ checksum and attaching it either as a header or a trailer.) Here's [an example from the QLDB SDK of creating a body] from inputs and inserting it into the request to be sent: -```rust +```rust,ignore let body = aws_smithy_http::body::SdkBody::from( crate::operation_ser::serialize_operation_crate_operation_send_command(&self)?, ); @@ -52,7 +52,7 @@ body until you've sent the request. Any metadata that needs to be calculated by trailers. Additionally, some metadata, like `Content-Length`, can't be sent as a trailer at all. [MDN maintains a helpful list] of metadata that can only be sent as a header. -```rust +```rust,ignore // When trailers are set, we must send an AWS-specific header that lists them named `x-amz-trailer`. // For example, when sending a SHA256 checksum as a trailer, // we have to send an `x-amz-trailer` header telling the service to watch out for it: @@ -73,7 +73,7 @@ a request body for `aws-chunked` requires us to know the length of each chunk we have to prefix each chunk with its size in bytes, represented by one or more [hexadecimal] digits. To close the body, we send a final chunk with a zero. For example, the body "Hello world" would look like this when encoded: -``` +```text B\r\n Hello world\r\n 0\r\n @@ -120,7 +120,7 @@ When using `aws-chunked` encoding, the trailers have to be appended to the body relying on the `poll_trailers` method. The working `http_body::Body` implementation of an `aws-chunked` encoded body looked like this: -```rust +```rust,ignore impl Body for AwsChunkedBody { type Data = Bytes; type Error = aws_smithy_http::body::Error; @@ -222,14 +222,14 @@ been read. be visible when printing with the `Debug` impl. Case in point was an error I was getting because of the `is_end_stream` issue. When `Debug` printed, the error looked like this: - ``` + ```rust,ignore DispatchFailure(ConnectorError { err: hyper::Error(User(Body), hyper::Error(BodyWriteAborted)), kind: User }) ``` That wasn't too helpful for me on its own. I looked into the `hyper` source code and found that the `Display` impl contained a helpful message, so I matched into the error and printed the `hyper::Error` with the `Display` impl: - ``` + ```markdown user body write aborted: early end, expected 2 more bytes' ``` @@ -239,7 +239,7 @@ been read. being sent out by the SDK as I was working on it. The Rust SDK supports setting endpoints for request. This is often used to send requests to something like [LocalStack], but I used it to send request to `localhost` instead: - ```rust + ```rust,ignore #[tokio::test] async fn test_checksum_on_streaming_request_against_s3() { let sdk_config = aws_config::from_env() @@ -262,7 +262,7 @@ been read. The echo server was based off of an [axum] example and looked like this: - ```rust + ```rust,ignore use axum::{ body::{Body, Bytes}, http::{request::Parts, Request, StatusCode}, diff --git a/design/src/overview.md b/design/src/overview.md index 975532c614..6ae40d7358 100644 --- a/design/src/overview.md +++ b/design/src/overview.md @@ -13,7 +13,7 @@ The design builds on the learnings, ideas, hard work, and GitHub issues of the 1 The Rust SDK is "modular" meaning that each AWS service is its own crate. Each crate provides two layers to access the service: 1. The "fluent" API. For most use cases, a high level API that ties together connection management and serialization will be the quickest path to success. -```rust +```rust,ignore #[tokio::main] async fn main() { let client = dynamodb::Client::from_env(); @@ -27,7 +27,7 @@ async fn main() { 2. The "low-level" API: It is also possible for customers to assemble the pieces themselves. This offers more control over operation construction & dispatch semantics: -```rust +```rust,ignore #[tokio::main] async fn main() { let conf = dynamodb::Config::builder().build(); diff --git a/design/src/rfcs/rfc0001_shared_config.md b/design/src/rfcs/rfc0001_shared_config.md index bd2a04ebbe..b0abd77500 100644 --- a/design/src/rfcs/rfc0001_shared_config.md +++ b/design/src/rfcs/rfc0001_shared_config.md @@ -37,7 +37,7 @@ tokio = { version = "1", features = ["full"] } Let's write a small example project to list tables: -```rust +```rust,ignore use aws_sdk_dynamodb as dynamodb; #[tokio::main] @@ -58,7 +58,7 @@ Next, we'll explore some other ways to configure the SDK. Perhaps you want to ov environment with your region. In this case, we'll want more control over how we load config, using `aws_config::from_env()` directly: -```rust +```rust,ignore use aws_sdk_dynamodb as dynamodb; #[tokio::main] @@ -88,7 +88,7 @@ tokio = { version = "1", features = ["full"] } Then, we can use the shared configuration to build both service clients. The region override will apply to both clients: -```rust +```rust,ignore use aws_sdk_dynamodb as dynamodb; use aws_sdk_polly as polly; @@ -135,7 +135,7 @@ To do this, implement the `ProvideCredentials` trait. > NOTE: `aws_types::Credentials` already implements `ProvideCredentials`. If you want to use the SDK with static credentials, you're already done! -```rust +```rust,ignore use aws_types::credentials::{ProvideCredentials, provide_credentials::future, Result}; struct MyCustomProvider; @@ -160,7 +160,7 @@ impl ProvideCredentials for MyCustomProvider { After writing your custom provider, you'll use it in when constructing the configuration: -```rust +```rust,ignore #[tokio::main] async fn main() { let config = aws_config::from_env().credentials_provider(MyCustomProvider).load().await; @@ -192,7 +192,7 @@ however, they won't have any default resolvers built in. Each AWS config will im This RFC proposes adding region and credentials providers support to the shared config. A future RFC will propose integration with HTTP settings, HTTPs connectors, and async sleep. -```rust +```rust,ignore struct Config { // private fields ... @@ -239,7 +239,7 @@ uses `Config` that is incompatible, they will get confusing compiler errors. An example of a problematic set of dependent versions: -``` +```markdown ┌─────────────────┐ ┌───────────────┐ │ aws-types = 0.1 │ │aws-types= 0.2 │ └─────────────────┘ └───────────────┘ diff --git a/design/src/rfcs/rfc0002_http_versions.md b/design/src/rfcs/rfc0002_http_versions.md index 3c8b87ab6e..1acfe48f39 100644 --- a/design/src/rfcs/rfc0002_http_versions.md +++ b/design/src/rfcs/rfc0002_http_versions.md @@ -45,7 +45,7 @@ around the underlying connector. When constructing operation builders, this hand given to the new builder instances so that their `send()` calls can initiate a request. The generated fluent client code ends up looking like this: -```rust +```rust,ignore struct Handle { client: aws_smithy_client::Client, conf: crate::Config, @@ -59,7 +59,7 @@ pub struct Client { Functions are generated per operation on the fluent client to gain access to the individual operation builders. For example: -```rust +```rust,ignore pub fn assume_role(&self) -> fluent_builders::AssumeRole { fluent_builders::AssumeRole::new(self.handle.clone()) } @@ -68,7 +68,7 @@ pub fn assume_role(&self) -> fluent_builders::AssumeRole { The fluent operation builders ultimately implement `send()`, which chooses the one and only Smithy client out of the handle to make the request with: -```rust +```rust,ignore pub struct AssumeRole { handle: std::sync::Arc>, inner: crate::input::assume_role_input::Builder, @@ -86,7 +86,7 @@ impl AssumeRole where ...{ Smithy clients are constructed from a connector, as shown: -```rust +```rust,ignore let connector = Builder::new() .https() .middleware(...) @@ -117,7 +117,7 @@ so that alternate HTTP implementations can be used, or so that a fake implementa To accomplish this, `SharedConfig` will have a `make_connector` member. A customer would configure it as such: -```rust +```rust,ignore let config = some_shared_config_loader() .with_http_settings(my_http_settings) .with_make_connector(|reqs: &MakeConnectorRequirements| { @@ -170,7 +170,7 @@ Smithy client. This cache needs to be adjusted to: To accomplish this, the `Handle` will hold a cache that is optimized for many reads and few writes: -```rust +```rust,ignore #[derive(Debug, Hash, Eq, PartialEq)] struct ConnectorKey { http_settings: HttpSettings, @@ -194,7 +194,7 @@ For cases where it is not, the custom connector type can host its own `dyn Trait The `HttpRequirements` struct will hold `HttpSettings` as copy-on-write so that it can be used for cache lookup without having to clone `HttpSettings`: -```rust +```rust,ignore struct HttpRequirements<'a> { http_settings: Cow<'a, HttpSettings>, http_version: HttpVersion, @@ -223,7 +223,7 @@ HTTP setting overrides are implemented. This doc is not attempting to solve that In the fluent client, this will look as follows: -```rust +```rust,ignore impl AssumeRole where ... { pub async fn send(self) -> Result> where ... { let input = self.create_input()?; diff --git a/design/src/rfcs/rfc0003_presigning_api.md b/design/src/rfcs/rfc0003_presigning_api.md index e8c6cef4bc..c75230751c 100644 --- a/design/src/rfcs/rfc0003_presigning_api.md +++ b/design/src/rfcs/rfc0003_presigning_api.md @@ -37,7 +37,7 @@ default to `SystemTime::now()`. Construction `PresigningConfig` can be done with a builder, but a `PresigningConfig::expires_in` convenience function will be provided to bypass the builder for the most frequent use-case. -```rust +```rust,ignore #[non_exhaustive] #[derive(Debug, Clone)] pub struct PresigningConfig { @@ -85,7 +85,7 @@ The generated fluent builders for operations that support presigning will have a in addition to `send()` that will return a presigned URL rather than sending the request. For S3's GetObject, the usage of this will look as follows: -```rust +```rust,ignore let config = aws_config::load_config_from_environment().await; let client = s3::Client::new(&config); let presigning_config = PresigningConfig::expires_in(Duration::from_secs(86400)); @@ -123,7 +123,7 @@ Even though generating a presigned URL through the fluent client doesn't necessi it will be clearer that this is the case by allowing the creation of presigned URLs directly from an input. This would look as follows: -```rust +```rust,ignore let config = aws_config::load_config_from_environment().await; let presigning_config = PresigningConfig::expires_in(Duration::from_secs(86400)); let presigned: PresignedRequest = GetObjectInput::builder() diff --git a/design/src/rfcs/rfc0004_retry_behavior.md b/design/src/rfcs/rfc0004_retry_behavior.md index ee3672ca33..3da28afdc7 100644 --- a/design/src/rfcs/rfc0004_retry_behavior.md +++ b/design/src/rfcs/rfc0004_retry_behavior.md @@ -39,7 +39,7 @@ _The default number of retries is 3 as specified in the [AWS SDKs and Tools Refe Here's an example app that logs your AWS user's identity -```rust +```rust,ignore use aws_sdk_sts as sts; #[tokio::main] @@ -66,7 +66,7 @@ cargo run Here's an example app that creates a shared config with custom retry behavior and then logs your AWS user's identity -```rust +```rust,ignore use aws_sdk_sts as sts; use aws_types::retry_config::StandardRetryConfig; @@ -86,7 +86,7 @@ async fn main() -> Result<(), sts::Error> { Here's an example app that creates a service-specific config with custom retry behavior and then logs your AWS user's identity -```rust +```rust,ignore use aws_sdk_sts as sts; use aws_types::retry_config::StandardRetryConfig; @@ -107,7 +107,7 @@ async fn main() -> Result<(), sts::Error> { Here's an example app that creates a shared config that disables retries and then logs your AWS user's identity -```rust +```rust,ignore use aws_sdk_sts as sts; use aws_types::config::Config; @@ -125,7 +125,7 @@ async fn main() -> Result<(), sts::Error> { Retries can also be disabled by explicitly passing the `RetryConfig::NoRetries` enum variant to the `retry_config` builder method: -```rust +```rust,ignore use aws_sdk_sts as sts; use aws_types::retry_config::RetryConfig; diff --git a/design/src/rfcs/rfc0008_paginators.md b/design/src/rfcs/rfc0008_paginators.md index a387af4b09..fc0a0c8b6e 100644 --- a/design/src/rfcs/rfc0008_paginators.md +++ b/design/src/rfcs/rfc0008_paginators.md @@ -23,7 +23,7 @@ original input, but with the field marked `inputToken` to the value of `outputTo Usage example: -```rust +```rust,ignore let paginator = client .list_tables() .paginate() @@ -41,7 +41,7 @@ Paginators are lazy and only retrieve pages when polled by a client. Paginators will be generated into the `paginator` module of service crates. Currently, paginators are _not_ feature gated, but this could be considered in the future. A `paginator` struct captures 2 pieces of data: -```rust +```rust,ignore // dynamodb/src/paginator.rs struct ListTablesPaginator { // holds the low-level client and configuration @@ -55,7 +55,7 @@ struct ListTablesPaginator { In addition to the basic usage example above, when `pageSize` is modeled, customers can specify the page size during pagination: -```rust +```rust,ignore let mut tables = vec![]; let mut pages = client .list_tables() @@ -75,7 +75,7 @@ enables demand driven execution of a closure. A rendezvous channel is used which When modeled by Smithy, `page_size` which automatically sets the appropriate page_size parameter and `items()` which returns an automatically flattened paginator are also generated. **Note**: `page_size` directly sets the modeled parameter on the internal builder. This means that a value set for page size will override any previously set value for that field. -```rust +```rust,ignore // Generated paginator for ListTables impl ListTablesPaginator { @@ -166,7 +166,7 @@ regressions in the generated code which would break users. The `builders` generated by ergonomic clients will gain the following method, if they represent an operation that implements the `Paginated` trait: -```rust +```rust,ignore /// Create a paginator for this request /// /// Paginators are used by calling [`send().await`](crate::paginator::ListTablesPaginator::send) which returns a [`Stream`](tokio_stream::Stream). diff --git a/design/src/rfcs/rfc0010_waiters.md b/design/src/rfcs/rfc0010_waiters.md index f738f0f357..3d0f80b291 100644 --- a/design/src/rfcs/rfc0010_waiters.md +++ b/design/src/rfcs/rfc0010_waiters.md @@ -10,7 +10,7 @@ than building out an entire polling mechanism manually. At the highest level, a waiter is a simple polling loop (pseudo-Rust): -```rust +```rust,ignore // Track state that contains the number of attempts made and the previous delay let mut state = initial_state(); @@ -74,7 +74,7 @@ Waiter API To invoke a waiter, customers will only need to invoke a single function on the AWS Client. For example, if waiting for a S3 bucket to exist, it would look like the following: -```rust +```rust,ignore // Request bucket creation client.create_bucket() .bucket_name("my-bucket") @@ -93,7 +93,7 @@ that will start the polling and return a future. To avoid name conflicts with other API methods, the waiter functions can be added to the client via trait: -```rust +```rust,ignore pub trait WaitUntilBucketExists { fn wait_until_bucket_exists(&self) -> crate::waiter::bucket_exists::Builder; } @@ -107,7 +107,7 @@ Waiter Implementation A waiter trait implementation will merely return a fluent builder: -```rust +```rust,ignore impl WaitUntilBucketExists for Client { fn wait_until_bucket_exists(&self) -> crate::waiter::bucket_exists::Builder { crate::waiter::bucket_exists::Builder::new() @@ -117,7 +117,7 @@ impl WaitUntilBucketExists for Client { This builder will have a short `send()` function to kick off the actual waiter implementation: -```rust +```rust,ignore impl Builder { // ... existing fluent builder codegen can be reused to create all the setters and constructor @@ -134,7 +134,7 @@ This wait function needs to, in a loop similar to the pseudo-code in the beginni convert the given input into an operation, replace the default response classifier on it with a no-retry classifier, and then determine what to do next based on that classification: -```rust +```rust,ignore pub async fn wait( handle: Arc, retry::Standard>>, input: HeadBucketInput, diff --git a/design/src/rfcs/rfc0013_body_callback_apis.md b/design/src/rfcs/rfc0013_body_callback_apis.md index b6b8e93e60..ea9b024c1d 100644 --- a/design/src/rfcs/rfc0013_body_callback_apis.md +++ b/design/src/rfcs/rfc0013_body_callback_apis.md @@ -9,7 +9,7 @@ Adding a callback API to `ByteStream` and `SdkBody` will enable developers using *Note that comments starting with '//' are not necessarily going to be included in the actual implementation and are intended as clarifying comments for the purposes of this RFC.* -```rust +```rust,ignore // in aws_smithy_http::callbacks... /// A callback that, when inserted into a request body, will be called for corresponding lifecycle events. @@ -41,7 +41,7 @@ The changes we need to make to `ByteStream`: *(The current version of `ByteStream` and `Inner` can be seen [here][ByteStream impls].)* -```rust +```rust,ignore // in `aws_smithy_http::byte_stream`... // We add a new method to `ByteStream` for inserting callbacks @@ -68,7 +68,7 @@ The changes we need to make to `SdkBody`: *(The current version of `SdkBody` can be seen [here][SdkBody impls].)* -```rust +```rust,ignore // In aws_smithy_http::body... #[pin_project] @@ -243,7 +243,7 @@ What follows is a simplified example of how this API could be used to introduce the checksum of some data and then returns the checksum of that data when `trailers` is called. This is fine because it's being used to calculate the checksum of a streaming body for a request. -```rust +```rust,ignore #[derive(Default)] struct Crc32cChecksumCallback { state: Option, diff --git a/design/src/rfcs/rfc0014_timeout_config.md b/design/src/rfcs/rfc0014_timeout_config.md index d4295f379f..52b1496ec0 100644 --- a/design/src/rfcs/rfc0014_timeout_config.md +++ b/design/src/rfcs/rfc0014_timeout_config.md @@ -99,7 +99,7 @@ Timeouts are achieved by racing a future against a `tokio::time::Sleep` future. _View [AwsMiddleware] in GitHub_ -```rust +```rust,ignore #[derive(Debug, Default)] #[non_exhaustive] pub struct AwsMiddleware; @@ -127,7 +127,7 @@ The above code is only included for context. This RFC doesn't define any timeout _View [aws_smithy_client::Client::call_raw] in GitHub_ -```rust +```rust,ignore impl Client where C: bounds::SmithyConnector, @@ -175,7 +175,7 @@ The **HTTP Request Timeout For A Single Attempt** and **HTTP Request Timeout For The resulting code would look like this: -```rust +```rust,ignore impl Client where C: bounds::SmithyConnector, diff --git a/design/src/rfcs/rfc0015_using_features_responsibly.md b/design/src/rfcs/rfc0015_using_features_responsibly.md index 2b018c64f4..336b25ca80 100644 --- a/design/src/rfcs/rfc0015_using_features_responsibly.md +++ b/design/src/rfcs/rfc0015_using_features_responsibly.md @@ -94,7 +94,7 @@ Conditionally compiling code when a feature is **not** activated can make it har One case where using `not` is acceptable is when providing a fallback when no features are set: -```rust +```rust,ignore #[cfg(feature = "rt-tokio")] pub fn default_async_sleep() -> Option> { Some(sleep_tokio()) diff --git a/design/src/rfcs/rfc0016_flexible_checksum_support.md b/design/src/rfcs/rfc0016_flexible_checksum_support.md index aa0851c619..ca906057aa 100644 --- a/design/src/rfcs/rfc0016_flexible_checksum_support.md +++ b/design/src/rfcs/rfc0016_flexible_checksum_support.md @@ -25,7 +25,7 @@ TLDR; This refactor of aws-smithy-checksums: - **Adds `fn checksum_header_name_to_checksum_algorithm`:** a function that's used in generated code when creating a checksum-validating response body. - **Add new checksum-related "body wrapping" HTTP body types**: These are defined in the `body` module and will be shown later in this RFC. -```rust +```rust,ignore // In aws-smithy-checksums/src/lib.rs //! Checksum calculation and verification callbacks @@ -489,7 +489,7 @@ When creating a checksum-validated request with an in-memory request body, we ca We will accomplish this by wrapping the `SdkBody` that requires validation within a `ChecksumBody`. Afterwards, we'll need to wrap the `ChecksumBody` in yet another layer which we'll discuss in the [`AwsChunkedBody` and `AwsChunkedBodyOptions`](#awschunkedbody-and-awschunkedbodyoptions) section. -```rust +```rust,ignore // In aws-smithy-checksums/src/body.rs use crate::{new_checksum, Checksum}; @@ -654,7 +654,7 @@ impl http_body::Body for ChecksumBody { Users may request checksum validation for response bodies. That capability is provided by `ChecksumValidatedBody`, which will calculate a checksum as the response body is being read. Once all data has been read, the calculated checksum is compared to a precalculated checksum set during body creation. If the checksums don't match, then the body will emit an error. -```rust +```rust,ignore // In aws-smithy-checksums/src/body.rs /// A response body that will calculate a checksum as it is read. If all data is read and the /// calculated checksum doesn't match a precalculated checksum, this body will emit an @@ -838,7 +838,7 @@ _**NOTES:**_ This encoding scheme is performed by `AwsChunkedBody` and configured with `AwsChunkedBodyOptions`. -```rust +```rust,ignore // In aws-http/src/content_encoding.rs use aws_smithy_checksums::body::ChecksumBody; use aws_smithy_http::body::SdkBody; @@ -1264,7 +1264,7 @@ Setting `STREAMING-UNSIGNED-PAYLOAD-TRAILER` tells the signer that we're sending We can achieve this by: - Adding a new variant to `SignableBody`: - ```rust + ```rust,ignore /// A signable HTTP request body #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] @@ -1279,7 +1279,7 @@ We can achieve this by: } ``` - Updating the `CanonicalRequest::payload_hash` method to include the new `SignableBody` variant: - ```rust + ```rust,ignore fn payload_hash<'b>(body: &'b SignableBody<'b>) -> Cow<'b, str> { // Payload hash computation // @@ -1300,7 +1300,7 @@ We can achieve this by: } ``` - *(in generated code)* Inserting the `SignableBody` into the request property bag when making a checksum-verified streaming request: - ```rust + ```rust,ignore if self.checksum_algorithm.is_some() { request .properties_mut() @@ -1315,7 +1315,7 @@ It's possible to send `aws-chunked` requests where each chunk is signed individu In order to avoid writing lots of Rust in Kotlin, I have implemented request and response building functions as inlineables: - Building checksum-validated requests with in-memory request bodies: - ```rust + ```rust,ignore // In aws/rust-runtime/aws-inlineable/src/streaming_body_with_checksum.rs /// Given a `&mut http::request::Request`, and checksum algorithm name, calculate a checksum and /// then modify the request to include the checksum as a header. @@ -1344,7 +1344,7 @@ In order to avoid writing lots of Rust in Kotlin, I have implemented request and } ``` - Building checksum-validated requests with streaming request bodies: - ```rust + ```rust,ignore /// Given an `http::request::Builder`, `SdkBody`, and a checksum algorithm name, return a /// `Request` with checksum trailers where the content is `aws-chunked` encoded. pub fn build_checksum_validated_request_with_streaming_body( @@ -1395,7 +1395,7 @@ In order to avoid writing lots of Rust in Kotlin, I have implemented request and } ``` - Building checksum-validated responses: - ```rust + ```rust,ignore /// Given a `Response`, checksum algorithm name, and pre-calculated checksum, return a /// `Response` where the body will processed with the checksum algorithm and checked /// against the pre-calculated checksum. diff --git a/design/src/rfcs/rfc0017_customizable_client_operations.md b/design/src/rfcs/rfc0017_customizable_client_operations.md index e91f4d625b..260635b799 100644 --- a/design/src/rfcs/rfc0017_customizable_client_operations.md +++ b/design/src/rfcs/rfc0017_customizable_client_operations.md @@ -10,7 +10,7 @@ the SDK has no easy way to accomplish this. At time of writing, the lower level client has to be used to create an operation, and then the HTTP request augmented on that operation type. For example: -```rust +```rust,ignore let input = SomeOperationInput::builder().some_value(5).build()?; let operation = { @@ -52,7 +52,7 @@ The code generated fluent builders returned by the fluent client should have a m similar to `send`, but that returns a customizable request. The customer experience should look as follows: -```rust +```rust,ignore let response = client.some_operation() .some_value(5) .customize() @@ -69,7 +69,7 @@ let response = client.some_operation() This new async `customize` method would return the following: -```rust +```rust,ignore pub struct CustomizableOperation { handle: Arc, operation: Operation, @@ -137,7 +137,7 @@ HTTP request. The `CustomizableOperation` type will then mirror these functions so that the experience can look as follows: -```rust +```rust,ignore let mut operation = client.some_operation() .some_value(5) .customize() @@ -167,7 +167,7 @@ Alternatively, the name `build` could be used, but this increases the odds that customers won't realize that they can call `send` directly, and then call a longer `build`/`send` chain when customization isn't needed: -```rust +```rust,ignore client.some_operation() .some_value() .build() // Oops, didn't need to do this @@ -177,7 +177,7 @@ client.some_operation() vs. -```rust +```rust,ignore client.some_operation() .some_value() .send() diff --git a/design/src/rfcs/rfc0018_logging_sensitive.md b/design/src/rfcs/rfc0018_logging_sensitive.md index 4ae6a27dd3..9598c823c4 100644 --- a/design/src/rfcs/rfc0018_logging_sensitive.md +++ b/design/src/rfcs/rfc0018_logging_sensitive.md @@ -46,7 +46,7 @@ Each of these configurable parts must therefore be logged cautiously. It would be unfeasible to forbid the logging of sensitive data all together using the type system. With the current API, the customer will always have an opportunity to log a request containing sensitive data before it enters the `Service>` that we provide to them. -```rust +```rust,ignore // The API provides us with a `Service>` let app: Router = OperationRegistryBuilder::default().build().expect("unable to build operation registry").into(); @@ -88,7 +88,7 @@ Developers might want to observe sensitive data for debugging purposes. It shoul To prevent excessive branches such as -```rust +```rust,ignore if cfg!(feature = "unredacted-logging") { debug!(%data, "logging here"); } else { @@ -98,7 +98,7 @@ if cfg!(feature = "unredacted-logging") { the following wrapper should be provided from a runtime crate: -```rust +```rust,ignore pub struct Sensitive(T); impl Debug for Sensitive @@ -130,7 +130,7 @@ where In which case the branch above becomes -```rust +```rust,ignore debug!(sensitive_data = %Sensitive(data)); ``` @@ -168,7 +168,7 @@ structure Stocked { should generate the following -```rust +```rust,ignore // NOTE: This code is intended to show behavior - it does not compile pub struct InventoryLogging { @@ -226,7 +226,7 @@ This logging middleware should be applied outside of the [OperationHandler](http An easy position to apply the logging middleware is illustrated below in the form of `Logging{Operation}::new`: -```rust +```rust,ignore let empty_operation = LoggingEmptyOperation::new(operation(registry.empty_operation)); let get_pokemon_species = LoggingPokemonSpecies::new(operation(registry.get_pokemon_species)); let get_server_statistics = LoggingServerStatistics::new(operation(registry.get_server_statistics)); @@ -273,7 +273,7 @@ Request extensions can be used to adjoin data to a Request as it passes through These can be used to provide data to middleware interested in logging potentially sensitive data. -```rust +```rust,ignore struct Sensitivity { /* Data concerning which parts of the request are sensitive */ } @@ -301,7 +301,7 @@ impl Service> for Middleware { A middleware layer must be code generated (much in the same way as the logging middleware) which is dedicated to inserting the `Sensitivity` struct into the extensions of each incoming request. -```rust +```rust,ignore impl Service> for SensitivityInserter where S: Service> @@ -333,7 +333,7 @@ where It is possible that sensitivity is a parameter passed to middleware during construction. This is similar in nature to [Use Request Extensions](#use-request-extensions) except that the `Sensitivity` is passed to middleware during construction. -```rust +```rust,ignore struct Middleware { inner: S, sensitivity: Sensitivity diff --git a/design/src/rfcs/rfc0019_event_streams_errors.md b/design/src/rfcs/rfc0019_event_streams_errors.md index 35274276f0..83311d902e 100644 --- a/design/src/rfcs/rfc0019_event_streams_errors.md +++ b/design/src/rfcs/rfc0019_event_streams_errors.md @@ -16,7 +16,7 @@ The user experience if this RFC is implemented ---------------------------------------------- In the current version of smithy-rs, customers who want to use errors in event streams need to use them as so: -```rust +```rust,ignore stream! { yield Ok(EventStreamUnion::ErrorVariant ...) } @@ -27,7 +27,7 @@ it does not signal termination and thus does not complete the stream. This RFC proposes to make changes to: * terminate the stream upon receiving a modeled error * change the API so that customers will write their business logic in a more Rust-like experience: -```rust +```rust,ignore stream! { yield Err(EventStreamUnionError::ErrorKind ...) } @@ -130,7 +130,7 @@ Wherever irrelevant, documentation and other lines are stripped out from the cod The error in `AttemptCapturingPokemonEvent` is modeled as follows. On the client, -```rust +```rust,ignore pub struct AttemptCapturingPokemonEventError { pub kind: AttemptCapturingPokemonEventErrorKind, pub(crate) meta: aws_smithy_types::Error, @@ -142,7 +142,7 @@ pub enum AttemptCapturingPokemonEventErrorKind { ``` On the server, -```rust +```rust,ignore pub enum AttemptCapturingPokemonEventError { MasterBallUnsuccessful(crate::error::MasterBallUnsuccessful), } @@ -160,7 +160,7 @@ On the other side, the `Receiver<>` needs to terminate the stream upon [receivin A terminated stream has [no more data](https://github.com/awslabs/smithy-rs/blob/8f7e03ff8a84236955a65dba3d21c4bdbf17a9f4/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs#L38) and will always be a [bug](https://github.com/awslabs/smithy-rs/blob/8f7e03ff8a84236955a65dba3d21c4bdbf17a9f4/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs#L54) to use it. An example of how errors can be used on clients, extracted from [this test](https://github.com/awslabs/smithy-rs/blob/8f7e03ff8a84236955a65dba3d21c4bdbf17a9f4/rust-runtime/aws-smithy-http-server/examples/pokemon_service/tests/simple_integration_test.rs#L100): -```rust +```rust,ignore yield Err(AttemptCapturingPokemonEventError::new( AttemptCapturingPokemonEventErrorKind::MasterBallUnsuccessful(MasterBallUnsuccessful::builder().build()), Default::default() diff --git a/design/src/rfcs/rfc0020_service_builder.md b/design/src/rfcs/rfc0020_service_builder.md index 35bf97f1f9..c6e18b9c10 100644 --- a/design/src/rfcs/rfc0020_service_builder.md +++ b/design/src/rfcs/rfc0020_service_builder.md @@ -45,7 +45,7 @@ We have purposely omitted details from the model that are unimportant to describ Here is a quick example of what a customer might write when using the service builder: -```rust +```rust,ignore async fn handler0(input: Operation0Input) -> Operation0Output { todo!() } @@ -71,7 +71,7 @@ During the survey we touch on the major mechanisms used to achieve this API. A core concept in the service builder is the `Handler` trait: -```rust +```rust,ignore pub trait Handler { async fn call(self, req: http::Request) -> http::Response; } @@ -81,7 +81,7 @@ Its purpose is to provide an even interface over closures of the form `FnOnce({O We generate `Handler` implementations for said closures in [ServerOperationHandlerGenerator.kt](https://github.com/awslabs/smithy-rs/blob/458eeb63b95e6e1e26de0858457adbc0b39cbe4e/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt): -```rust +```rust,ignore impl Handler<(), Operation0Input> for Fun where Fun: FnOnce(Operation0Input) -> Fut, @@ -122,7 +122,7 @@ The `request.extensions().get::()` present in the `Fun: FnOnce(Operation0Inpu To convert the closures described above into a `Service` an `OperationHandler` is used: -```rust +```rust,ignore pub struct OperationHandler { handler: H, } @@ -151,7 +151,7 @@ The service builder we provide to the customer is the `OperationRegistryBuilder` Currently, the reference model would generate the following `OperationRegistryBuilder` and `OperationRegistry`: -```rust +```rust,ignore pub struct OperationRegistryBuilder { operation1: Option, operation2: Option, @@ -165,7 +165,7 @@ pub struct OperationRegistry { The `OperationRegistryBuilder` includes a setter per operation, and a fallible `build` method: -```rust +```rust,ignore impl OperationRegistryBuilder { pub fn operation0(mut self, value: Op0) -> Self { self.operation0 = Some(value); @@ -188,7 +188,7 @@ impl OperationRegistryBuilder { The `OperationRegistry` does not include any methods of its own, however it does enjoy a `From for Router` implementation: -```rust +```rust,ignore impl From> for Router where Op0: Handler, @@ -223,7 +223,7 @@ where The [aws_smithy_http::routing::Router](https://github.com/awslabs/smithy-rs/blob/458eeb63b95e6e1e26de0858457adbc0b39cbe4e/rust-runtime/aws-smithy-http-server/src/routing/mod.rs#L58-L60) provides the protocol aware routing of requests to their target , it exists as -```rust +```rust,ignore pub struct Route { service: Box>, } @@ -242,7 +242,7 @@ pub struct Router { and enjoys the following `Service` implementation: -```rust +```rust,ignore impl Service for Router { type Response = http::Response; @@ -268,7 +268,7 @@ impl Service for Router Along side the protocol specific constructors, `Router` includes a `layer` method. This provides a way for the customer to apply a `tower::Layer` to all routes. For every protocol, `Router::layer` has the approximately the same behavior: -```rust +```rust,ignore let new_routes = old_routes .into_iter() // Apply the layer @@ -299,7 +299,7 @@ To identify where the implementations should differ we should classify in what w In `axum` there is a notion of [Extractor](https://docs.rs/axum/latest/axum/extract/index.html), which allows the customer to easily define a decomposition of an incoming `http::Request` by specifying the arguments to the handlers. For example, -```rust +```rust,ignore async fn request(Json(payload): Json, Query(params): Query>, headers: HeaderMap) { todo!() } @@ -350,7 +350,7 @@ The Smithy model not only specifies the `http::Request` decomposition and `http: This is in contrast to `axum`, where the user specifies the routing by use of various combinators included on the `axum::Router`, applied to other `tower::Service`s. In an `axum` application one might encounter the following code: -```rust +```rust,ignore let user_routes = Router::new().route("/:id", /* service */); let team_routes = Router::new().route("/", /* service */); @@ -366,7 +366,7 @@ Note that, in `axum` handlers are eagerly converted to a `tower::Service` (via ` Introducing state to handlers in `axum` is done in the same way as `smithy-rs`, described briefly in [Handlers](#handlers) - a layer is used to insert state into incoming `http::Request`s and the `Handler` implementation pops it out of the type map layer. In `axum`, if a customer wanted to scope state to all routes within `/users/` they are able to do the following: -```rust +```rust,ignore async fn handler(Extension(state): Extension) -> /* Return Type */ {} let api_routes = Router::new() @@ -390,7 +390,7 @@ As described in [Builder](#builder), the customer is required to perform two con As described in [Builder](#builder), the `OperationRegistryBuilder::build` method is fallible - it yields a runtime error when one of the handlers has not been set. -```rust +```rust,ignore pub fn build( self, ) -> Result, OperationRegistryBuilderError> { @@ -403,7 +403,7 @@ As described in [Builder](#builder), the `OperationRegistryBuilder::build` metho We can do away with fallibility if we allow for on `Op0`, `Op1` to switch types during build and remove the `Option` from around the fields. The `OperationRegistryBuilder` then becomes -```rust +```rust,ignore struct OperationRegistryBuilder { operation_0: Op0, operation_1: Op1 @@ -444,19 +444,19 @@ The customer will now get a compile time error rather than a runtime error when To construct a `Router`, the customer must either give a type ascription -```rust +```rust,ignore let app: Router = /* Service builder */.into(); ``` or be explicit about the `Router` namespace -```rust +```rust,ignore let app = Router::from(/* Service builder */); ``` If we switch from a `From for Router` to a `build` method on `OperationRegistry` the customer may simply -```rust +```rust,ignore let app = /* Service builder */.build(); ``` @@ -478,7 +478,7 @@ Throughout this section we purposely ignore the existence of handlers accepting It's possible to make progress with a small changeset, by requiring the customer eagerly uses `OperationHandler::new` rather than it being applied internally within `From for Router` (see [Handlers](#handlers)). The setter would then become: -```rust +```rust,ignore pub struct OperationRegistryBuilder { operation1: Option, operation2: Option @@ -494,7 +494,7 @@ impl OperationRegistryBuilder { The API usage would then become -```rust +```rust,ignore async fn handler0(input: Operation0Input) -> Operation0Output { todo!() } @@ -514,7 +514,7 @@ Note that this requires that the `OperationRegistryBuilder` stores services, rat It is still possible to retain the original API which accepts `Handler` by introducing the following setters: -```rust +```rust,ignore impl OperationRegistryBuilder { fn operation0_handler(self, handler: H) -> OperationRegistryBuilder, Op2> { OperationRegistryBuilder { @@ -535,7 +535,7 @@ This does not solve (3), as the customer is not able to provide a `tower::Servic In order to achieve all three we model operations as middleware: -```rust +```rust,ignore pub struct Operation0 { inner: S, } @@ -572,7 +572,7 @@ A consequence of this is that the user `Operation0` must have two constructors: A brief example of how this might look: -```rust +```rust,ignore use tower::util::{ServiceFn, service_fn}; impl Operation0 { @@ -594,7 +594,7 @@ impl Operation0> { The API usage then becomes: -```rust +```rust,ignore async fn handler(input: Operation0Input) -> Operation0Output { todo!() } @@ -615,7 +615,7 @@ While [Attempt B](#approach-b-operations-as-middleware) solves all three problem Any solution which provides an `{Operation}` structure and wishes it to be accepted by multiple service builders must deal with this problem. We currently build one library per service and hence have duplicate structures when [service closures](https://awslabs.github.io/smithy/1.0/spec/core/model.html#service-closure) overlap. This means we wouldn't run into this problem today, but it would be a future obstruction if we wanted to reduce the amount of generated code. -```rust +```rust,ignore use tower::layer::util::{Stack, Identity}; use tower::util::{ServiceFn, service_fn}; @@ -709,7 +709,7 @@ Currently the `Router` stores `Box`, allows us to write: -```rust +```rust,ignore impl Router { fn layer(self, layer: &L) -> Router where @@ -775,7 +775,7 @@ This is compatible with [Protocol specific Routers](#protocol-specific-routers), With both of these changes the API would take the form: -```rust +```rust,ignore let service_0: Service0 = Service0::builder() /* use the setters */ .build() @@ -785,7 +785,7 @@ let service_0: Service0 = Service0::builder() With [Remove two-step build procedure](#remove-two-step-build-procedure), [Switch `From for Router` to a `OperationRegistry::build` method](#switch-fromoperationregistry-for-router-to-an-operationregistrybuild-method), and [Statically check for missing Handlers](#statically-check-for-missing-handlers) we obtain the following API: -```rust +```rust,ignore let service_0: Service0 = Service0::builder() /* use the setters */ .build(); @@ -795,7 +795,7 @@ let service_0: Service0 = Service0::builder() A combination of all the proposed transformations results in the following API: -```rust +```rust,ignore struct Context { /* fields */ } diff --git a/design/src/rfcs/rfc0022_error_context_and_compatibility.md b/design/src/rfcs/rfc0022_error_context_and_compatibility.md index cf03b2ed8d..c8791dbfaa 100644 --- a/design/src/rfcs/rfc0022_error_context_and_compatibility.md +++ b/design/src/rfcs/rfc0022_error_context_and_compatibility.md @@ -28,7 +28,7 @@ that this RFC will attempt to solve, and calls out what was done well, and what ### Case study: `InvalidFullUriError` To start, let's examine `InvalidFullUriError` (doc comments omitted): -```rust +```rust,ignore #[derive(Debug)] #[non_exhaustive] pub enum InvalidFullUriError { @@ -89,7 +89,7 @@ However, there are also a number of things that could be improved: Next, let's look at a much simpler error. The `ProfileParseError` is focused purely on the parsing logic for the SDK config file: -```rust +```rust,ignore #[derive(Debug, Clone)] pub struct ProfileParseError { location: Location, @@ -128,7 +128,7 @@ What could be improved: The SDK currently generates errors such as the following (from S3): -```rust +```rust,ignore #[non_exhaustive] pub enum Error { BucketAlreadyExists(BucketAlreadyExists), @@ -232,7 +232,7 @@ all errors in the public API for the Rust runtime crates and generated client cr Actionable errors are represented as enums. If an error variant has an error source or additional contextual information, it must use a separate context struct that is referenced via tuple in the enum. For example: -```rust +```rust,ignore // Good: new error types can be added in the future #[non_exhaustive] pub enum Error { @@ -296,7 +296,7 @@ adding a new error variant's source at a later date. The error `Display` implementation _must not_ include the source in its output: -```rust +```rust,ignore // Good impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -326,7 +326,7 @@ impl fmt::Display for Error { Informative errors must be represented as structs. If error messaging changes based on an underlying cause, then a private error kind enum can be used internally for this purpose. For example: -```rust +```rust,ignore #[derive(Debug)] pub struct InformativeError { some_additional_info: u32, @@ -356,13 +356,13 @@ In code where errors are logged rather than returned to the customer, the full e must be displayed. This will be made easy by placing a `DisplayErrorContext` struct in `aws-smithy-types` that is used as a wrapper to get the better error formatting: -```rust +```rust,ignore tracing::warn!(err = %DisplayErrorContext(err), "some message"); ``` This might be implemented as follows: -```rust +```rust,ignore #[derive(Debug)] pub struct DisplayErrorContext(pub E); diff --git a/design/src/rfcs/rfc0023_refine_builder.md b/design/src/rfcs/rfc0023_refine_builder.md index d93cd6bf77..1c0b6f9786 100644 --- a/design/src/rfcs/rfc0023_refine_builder.md +++ b/design/src/rfcs/rfc0023_refine_builder.md @@ -40,7 +40,7 @@ Constraints: Let's start by reviewing the API proposed in [RFC 20]. We will use the [Pokemon service] as our driving example throughout the RFC. This is what the startup code looks like: -```rust +```rust,ignore #[tokio::main] pub async fn main() { // [...] @@ -115,7 +115,7 @@ Given the above, we think that the impact of a runtime error is low enough to be Moving from a compile-time error to a runtime error does not require extensive refactoring. The definition of `PokemonServiceBuilder` goes from: -```rust +```rust,ignore pub struct PokemonServiceBuilder< Op1, Op2, @@ -145,7 +145,7 @@ pub struct PokemonServiceBuilder< to: -```rust +```rust,ignore pub struct PokemonServiceBuilder< Op1, Op2, @@ -176,7 +176,7 @@ pub struct PokemonServiceBuilder< All operation fields are now `Option`-wrapped. We introduce a new `MissingOperationsError` error to hold the names of the missing operations and their respective setter methods: -```rust +```rust,ignore #[derive(Debug)] pub struct MissingOperationsError { service_name: &'static str, @@ -208,7 +208,7 @@ We can also provide actionable suggestions: Rust beginners should be able to eas Let's take a second look at the (updated) definition of `PokemonServiceBuilder`: -```rust +```rust,ignore pub struct PokemonServiceBuilder< Op1, Op2, @@ -255,7 +255,7 @@ Let's consider a toy example: if a `check_database` flag is set to `true`, we wa The "obvious" solution would look somewhat like this: -```rust +```rust,ignore let check_database: bool = /* */; let app = if check_database { app.check_health(check_health) @@ -303,7 +303,7 @@ The developer has three options to move forward: I can't easily see a way to accomplish 1) using the current API. Pursuing 2) is straight-forward with a single conditional: -```rust +```rust,ignore let check_database: bool = /* */; let app = if check_database { app.check_health(check_health).build() @@ -314,7 +314,7 @@ let app = if check_database { It becomes more cumbersome when we have more than a single conditional: -```rust +```rust,ignore let check_database: bool = /* */; let include_cpu_statics: bool = /* */; match (check_database, include_cpu_statics) { @@ -339,7 +339,7 @@ match (check_database, include_cpu_statics) { A lot of repetition compared to the code for the "obvious" approach: -```rust +```rust,ignore let check_database: bool = /* */; let include_cpu_statics: bool = /* */; let app = if check_database { @@ -367,7 +367,7 @@ The service builder must be one of the arguments if we want to register handlers A first sketch: -```rust +```rust,ignore fn partial_setup(builder: PokemonServiceBuilder) -> PokemonServiceBuilder { /* */ } @@ -395,7 +395,7 @@ note: struct defined here, with at least 6 generic parameters: `Op1`, `Op2`, `Op We could try to nudge the compiler into inferring them: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder<_, _, _, _, _, _>, ) -> PokemonServiceBuilder<_, _, _, _, _, _> { @@ -421,7 +421,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures We must type it all out: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder { @@ -432,7 +432,7 @@ fn partial_setup( That compiles, at last. Let's try to register an operation handler now: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder { @@ -468,7 +468,7 @@ Can we get rid of them? Yes! Let's look at one possible approach: -```rust +```rust,ignore pub struct PokemonServiceBuilder { check_health: Option>, do_nothing: Option>, @@ -483,7 +483,7 @@ pub struct PokemonServiceBuilder { We no longer store the raw handlers inside `PokemonServiceBuilder`. We eagerly upgrade the operation handlers to a `Route` instance when they are registered with the builder. -```rust +```rust,ignore impl PokemonServiceBuilder { pub fn get_pokemon_species(mut self, handler: Handler) -> Self /* Complex trait bounds */ @@ -500,7 +500,7 @@ impl PokemonServiceBuilder { The existing API performs the upgrade when `build` is called, forcing `PokemonServiceBuilder` to store the raw handlers and keep two generic parameters around (`OpX` and `ExtsX`) for each operation. The proposed API requires plugins to be specified upfront, when creating an instance of the builder. They cannot be modified after a `PokemonServiceBuilder` instance has been built: -```rust +```rust,ignore impl PokemonService<()> { /// Constructs a builder for [`PokemonService`]. pub fn builder(plugin: Plugin) -> PokemonServiceBuilder { @@ -526,7 +526,7 @@ We have seen how cumbersome it is to break the startup logic into different func The new design prohibits the following invocation style: -```rust +```rust,ignore let plugin = ColorPlugin::new(); PokemonService::builder(plugin) // [...] @@ -548,7 +548,7 @@ There are no technical obstacles preventing us from implementing this API, but I We can provide developers with other mechanisms to register plugins for a single operation or a subset of operations without introducing ambiguity. For attaching additional plugins to a single operation, we could introduce a blanket `Pluggable` implementation for all operations in `aws-smithy-http-server`: -```rust +```rust,ignore impl Pluggable for Operation where Pl: Plugin { type Output = Operation; @@ -561,7 +561,7 @@ impl Pluggable for Operation where Pl: Plugin { @@ -631,7 +631,7 @@ The above leaves us with two unconstrained type parameters, `Operation` and `Ext Going back to the branching example: -```rust +```rust,ignore let check_database: bool = /* */; let builder = if check_database { builder.check_health(check_health) @@ -643,7 +643,7 @@ let app = builder.build(); In approach 1), we could leverage the `.boxed()` method to convert the actual `OpX` type into a `Box`, thus ensuring that both branches return the same type: -```rust +```rust,ignore let check_database: bool = /* */; let builder = if check_database { builder.check_health_operation(Operation::from_handler(check_health).boxed()) @@ -655,7 +655,7 @@ let app = builder.build(); The same cannot be done when conditionally registering a route, because on the `else` branch we cannot convert `MissingOperation` into a `Box` since `MissingOperation` doesn't implement `Upgradable` - the pillar on which we built all our compile-time safety story. -```rust +```rust,ignore // This won't compile! let builder = if check_database { builder.check_health_operation(Operation::from_handler(check_health).boxed()) @@ -666,7 +666,7 @@ let builder = if check_database { In approach 2), we can erase the whole builder in both branches when they both register a route: -```rust +```rust,ignore let check_database: bool = /* */; let boxed_builder = if check_database { builder.check_health(check_health).erase() @@ -682,7 +682,7 @@ but, like in approach 1), we will still get a type mismatch error if one of the Developers would still have to spell out all generic parameters when writing a function that takes in a builder as a parameter: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder { @@ -693,7 +693,7 @@ fn partial_setup( Writing the signature after having modified the builder becomes easier though. In approach 1), they can explicitly change the touched operation parameters to the boxed variant: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder< @@ -706,7 +706,7 @@ fn partial_setup( It becomes trickier in approach 2), since to retain compile-time safety on the builder we expect `erase` to map `MissingOperation` into `MissingOperation`. Therefore, we can't write something like this: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder, Route, Route, Route, Route, Route> { @@ -716,7 +716,7 @@ fn partial_setup( The compiler would reject it since it can't guarantee that all other operations can be erased to a `Route`. This is likely to require something along the lines of: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder<::Erased, ::Erased, ::Erased, ::Erased, ::Erased, ::Erased> @@ -742,7 +742,7 @@ We believe that the ergonomics advantages of the proposal advanced by this RFC o The `Pluggable` trait was an interesting development out of [RFC 20]: it allows you to attach methods to a service builder using an extension trait. -```rust +```rust,ignore /// An extension to service builders to add the `print()` function. pub trait PrintExt: aws_smithy_http_server::plugin::Pluggable { /// Causes all operations to print the operation name when called. @@ -760,7 +760,7 @@ pub trait PrintExt: aws_smithy_http_server::plugin::Pluggable { This pattern needs to be revisited if we want to move forward with this RFC, since new plugins cannot be registered after the builder has been instantiated. My recommendation would be to implement `Pluggable` for `PluginStack`, providing the same pattern ahead of the creation of the builder: -```rust +```rust,ignore // Currently you'd have to go for `PluginStack::new(IdentityPlugin, IdentityPlugin)`, // but that can be smoothed out even if this RFC isn't approved. let plugin_stack = PluginStack::default() diff --git a/design/src/rfcs/rfc0024_request_id.md b/design/src/rfcs/rfc0024_request_id.md index a10d5e92e7..5c06d10619 100644 --- a/design/src/rfcs/rfc0024_request_id.md +++ b/design/src/rfcs/rfc0024_request_id.md @@ -43,13 +43,13 @@ To aid customers already relying on clients' request IDs, there will be two type 1. Implementing `FromParts` for `Extension` gives customers the ability to write their handlers: -```rust +```rust,ignore pub async fn handler( input: input::Input, request_id: Extension, ) -> ... ``` -```rust +```rust,ignore pub async fn handler( input: input::Input, request_id: Extension, @@ -71,7 +71,7 @@ For privacy reasons, any format that provides service details should be avoided. The proposed format is to use UUID, version 4. A `Service` that inserts a RequestId in the extensions will be implemented as follows: -```rust +```rust,ignore impl Service> for ServerRequestIdProvider where S: Service>, @@ -96,7 +96,7 @@ For client request IDs, the process will be, in order: * Otherwise, None `Option` is used to distinguish whether a client had provided an ID or not. -```rust +```rust,ignore impl Service> for ClientRequestIdProvider where S: Service>, diff --git a/design/src/rfcs/rfc0025_constraint_traits.md b/design/src/rfcs/rfc0025_constraint_traits.md index a60b24e574..e3aaa32319 100644 --- a/design/src/rfcs/rfc0025_constraint_traits.md +++ b/design/src/rfcs/rfc0025_constraint_traits.md @@ -114,7 +114,7 @@ values and perform the _validation_ at request deserialization, we can wrapper [tuple struct] that _parses_ the string's value and is "tight" in the set of values it can accept: -```rust +```rust,ignore pub struct NiceString(String); impl TryFrom for NiceString { @@ -145,7 +145,7 @@ the [`?` operator for error propagation]. Each constrained struct will have a related `std::error::Error` enum type to signal the _first_ parsing failure, with one enum variant per applied constraint trait: -```rust +```rust,ignore pub mod nice_string { pub enum ConstraintViolation { /// Validation error holding the number of Unicode code points found, when a value between `1` and @@ -161,7 +161,7 @@ pub mod nice_string { `#[derive(Debug)]`, unless the shape also has the [`sensitive` trait], in which case we will just print the name of the struct: -```rust +```rust,ignore impl std::fmt::Debug for ConstraintViolation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut formatter = f.debug_struct("ConstraintViolation"); @@ -207,7 +207,7 @@ string Language the code the client generates when deserializing a string from a JSON document into the `Language` enum is (excerpt): -```rust +```rust,ignore ... match key.to_unescaped()?.as_ref() { "language" => { @@ -229,7 +229,7 @@ match key.to_unescaped()?.as_ref() { Note how the `String` gets converted to the enum via `Language::from()`. -```rust +```rust,ignore impl std::convert::From<&str> for Language { fn from(s: &str) -> Self { match s { diff --git a/design/src/rfcs/rfc0026_client_crate_organization.md b/design/src/rfcs/rfc0026_client_crate_organization.md index 672eed876d..5611d0ebfb 100644 --- a/design/src/rfcs/rfc0026_client_crate_organization.md +++ b/design/src/rfcs/rfc0026_client_crate_organization.md @@ -163,7 +163,7 @@ or that are required for the most frequent config changes (such as setting crede or changing the region/endpoint). Previously, the following were exported in root: -``` +```text . ├── AppName ├── Client @@ -182,7 +182,7 @@ need to be at the top-level, and will be moved into `crate::config`. `ErrorExt` `crate::error`, but `Error` will stay in the crate root so that customers that alias the SDK crate can easily reference it in their `Result`s: -```rust +```rust,ignore use aws_sdk_s3 as s3; fn some_function(/* ... */) -> Result<(), s3::Error> { diff --git a/design/src/rfcs/rfc0027_endpoints_20.md b/design/src/rfcs/rfc0027_endpoints_20.md index b5428c1ed1..90739d347b 100644 --- a/design/src/rfcs/rfc0027_endpoints_20.md +++ b/design/src/rfcs/rfc0027_endpoints_20.md @@ -96,7 +96,7 @@ global endpoint provider that can override different services. There is a single is shared across all services. However, this isn't the case for `Endpoints 2.0` where the trait actually has a generic parameter: -```rust +```rust,ignore pub trait ResolveEndpoint: Send + Sync { fn resolve_endpoint(&self, params: &T) -> Result; } @@ -129,7 +129,7 @@ This RFC proposes making the following changes: **Example: overriding the endpoint URI globally** -```rust +```rust,ignore async fn main() { let sdk_conf = aws_config::from_env().endpoint_url("http://localhost:8123").load().await; let dynamo = aws_sdk_dynamodb::Client::new(&sdk_conf); @@ -139,7 +139,7 @@ async fn main() { **Example: overriding the endpoint resolver for a service** -```rust +```rust,ignore /// Resolve to Localhost when an environment variable is set struct CustomDdbResolver; @@ -191,7 +191,7 @@ gated with documentation warning about stability. #### The Endpoint Struct -```rust +```rust,ignore // module: `aws_smithy_types::endpoint` // potential optimization to reduce / remove allocations for keys which are almost always static // this can also just be `String` @@ -221,7 +221,7 @@ pub struct Endpoint { To perform produce an `Endpoint` struct we have a generic `ResolveEndpoint` trait which will be both generic in terms of parameters and being "smithy-generic: -```rust +```rust,ignore // module: `smithy_types::endpoint` or `aws_smithy_client`?? pub trait ResolveEndpoint: Send + Sync { /// Resolves an `Endpoint` for `Params` @@ -242,7 +242,7 @@ the parameters are set, *not* how they are generated. **Example `Params` struct for S3:** -```rust +```rust,ignore #[non_exhaustive] #[derive(std::clone::Clone, std::cmp::PartialEq, std::fmt::Debug)] /// Configuration parameters for resolving the correct endpoint @@ -289,7 +289,7 @@ When an endpoint ruleset is present, Smithy will code generate an endpoint resol resolver **MUST** be a struct so that it can store/cache computations (such as a partition resolver that has compiled regexes). -```rust +```rust,ignore pub struct DefaultEndpointResolver { partition_resolver: PartitionResolver } @@ -539,7 +539,7 @@ An alternative design that could provide more flexibility is a context-aware end give context about the endpoint being returned. This would, for example, allow a customer to say explicitly "don't modify this endpoint": -```rust +```rust,ignore enum ContextualEndpoint { /// Just the URI please. Pass it into the default endpoint resolver as a baseline Uri { uri: Uri, immutable: bool }, diff --git a/design/src/rfcs/rfc0028_sdk_credential_cache_type_safety.md b/design/src/rfcs/rfc0028_sdk_credential_cache_type_safety.md index d16b24e226..719f2ef9fd 100644 --- a/design/src/rfcs/rfc0028_sdk_credential_cache_type_safety.md +++ b/design/src/rfcs/rfc0028_sdk_credential_cache_type_safety.md @@ -33,7 +33,7 @@ and wrapping the given (or default) credentials provider. The `CredentialsCache` would look as follows: -```rust +```rust,ignore enum Inner { Lazy(LazyConfig), // Eager doesn't exist today, so this is purely for illustration @@ -89,7 +89,7 @@ the `impl ProvideCredentials + 'static`. A sealed trait could be added to facili Customers that don't care about credential caching can configure credential providers without needing to think about it: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_provider(ImdsCredentialsProvider::builder().build()) .load() @@ -99,7 +99,7 @@ let sdk_config = aws_config::from_env() However, if they want to customize the caching, they can do so without modifying the credentials provider at all (in case they want to use the default): -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_cache(CredentialsCache::default_eager()) .load() @@ -141,7 +141,7 @@ without any caching logic, although this wouldn't be recommended and this provid in `aws-config`. Example configuration: -```rust +```rust,ignore // Compiles let sdk_config = aws_config::from_env() .credentials( @@ -162,7 +162,7 @@ let sdk_config = aws_config::from_env() Another method could be added to `ConfigLoader` that makes it easier to use the default cache: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_with_default_cache(ImdsCredentialsProvider::new()) .load() @@ -187,7 +187,7 @@ This alternative is similar to alternative A, except that the cache trait is dis that it's more apparent when mistakenly implementing the wrong trait for a custom credentials provider. A `CacheCredentials` trait would be added that looks as follows: -```rust +```rust,ignore pub trait CacheCredentials: Send + Sync + Debug { async fn cached(&self, now: SystemTime) -> Result; } @@ -216,7 +216,7 @@ but at the same time, doesn't make it impossible to add custom caching later. The idea is that there would be a struct called `CredentialsCache` that specifies the desired caching approach for a given credentials provider: -```rust +```rust,ignore pub struct LazyCache { credentials_provider: Arc, // ... @@ -280,7 +280,7 @@ than `impl ProvideCredentials + 'static`. A sealed trait could be added to facil Configuration would look as follows: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials(CredentialsCache::default_lazy(ImdsCredentialsProvider::builder().build())) .load() @@ -293,7 +293,7 @@ a use case, then a `CredentialsCache::NoCache` variant could be made. Like alternative A, a convenience method can be added to make using the default cache easier: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_with_default_cache(ImdsCredentialsProvider::builder().build()) .load() @@ -302,7 +302,7 @@ let sdk_config = aws_config::from_env() In the future if custom caching is added, it would look as follows: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials( CredentialsCache::custom(ImdsCredentialsProvider::builder().build(), MyCache::new()) @@ -316,7 +316,7 @@ from the config are needed to construct the cache (such as `sleep_impl`). Thus, setter would merely save off the `CredentialsCache` instance, and then when `load` is called, the complete `SharedCredentialsProvider` would be constructed: -```rust +```rust,ignore pub async fn load(self) -> SdkConfig { // ... let credentials_provider = self.credentials_cache.create_cache(sleep_impl); diff --git a/design/src/rfcs/rfc0029_new_home_for_cred_types.md b/design/src/rfcs/rfc0029_new_home_for_cred_types.md index 734ef7408c..e864bd7d5b 100644 --- a/design/src/rfcs/rfc0029_new_home_for_cred_types.md +++ b/design/src/rfcs/rfc0029_new_home_for_cred_types.md @@ -23,7 +23,7 @@ Problems -------- Here is how our attempt to implement the selected design in the preceding RFC can lead to an obstacle. Consider this code snippet we are planning to support: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_cache(CredentialsCache::lazy()) .load() @@ -34,7 +34,7 @@ let client = aws_sdk_s3::Client::new(&sdk_config); A `CredentialsCache` created by `CredentialsCache::lazy()` above will internally go through three crates before the variable `client` has been created: 1. `aws-config`: after it has been passed to `aws_config::ConfigLoader::credentials_cache` -```rust +```rust,ignore // in lib.rs impl ConfigLoader { @@ -47,7 +47,7 @@ impl ConfigLoader { } ``` 2. `aws-types`: after `aws_config::ConfigLoader::load` has passed it to `aws_types::sdk_config::Builder::credentials_cache` -```rust +```rust,ignore // in sdk_config.rs impl Builder { @@ -60,7 +60,7 @@ impl Builder { } ``` 3. `aws-sdk-s3`: after `aws_sdk_s3::Client::new` has been called with the variable `sdk_config` -```rust +```rust,ignore // in client.rs impl Client { @@ -72,7 +72,7 @@ impl Client { } ``` calls -```rust +```rust,ignore // in config.rs impl From<&aws_types::sdk_config::SdkConfig> for Builder { diff --git a/design/src/rfcs/rfc0030_serialization_and_deserialization.md b/design/src/rfcs/rfc0030_serialization_and_deserialization.md index 1e4c6c3bf1..46ad1a4cb6 100644 --- a/design/src/rfcs/rfc0030_serialization_and_deserialization.md +++ b/design/src/rfcs/rfc0030_serialization_and_deserialization.md @@ -170,7 +170,7 @@ Serde can distinguish each variant without a tag as each variant's content is di Builder types and non Builder types implement `Serialize` and `Deserialize` with derive macro. Example: -```rust +```rust,ignore #[cfg_attr( all(aws-sdk-unstable, feature = "serialize"), derive(serde::Serialize) @@ -220,7 +220,7 @@ Here is an example of data types affected by this decision: We considered serializing them as bytes, however, it could take some time for a stream to reach the end, and the resulting serialized data may be too big for itself to fit into the ram. Here is an example snippet. -```rust +```rust,ignore #[allow(missing_docs)] #[cfg_attr( all(aws-sdk-unstable, feature = "serde-serialize"), @@ -272,7 +272,7 @@ SDK does not have a method that allows users to use deserialized `Input`. Thus, we add a new method `fn set_fields` to `Client` types. This method accepts inputs and replaces all parameters that `Client` has with the new one. -```rust +```rust,ignore pub fn set_fields(mut self, input_type: path::to::input_type) -> path::to::input_type { self.inner = input_type; self @@ -310,7 +310,7 @@ To clarify, this is the same approach we took on `Data Type to skip` section. Either way, we will mention this on the generated docs to avoid surprising users. e.g. -```rust +```rust,ignore #[derive(serde::Serialize, serde::Deserialize)] struct OutputV1 { string_field: Option @@ -389,7 +389,7 @@ We believe that features implemented as part of this RFC do not produce a mislea # Appendix ## [Use Case Examples](UseCaseExamples) -```rust +```rust,ignore use aws_sdk_dynamodb::{Client, Error}; async fn example(read_builder: bool) -> Result<(), Error> { diff --git a/design/src/rfcs/rfc0031_providing_fallback_credentials_on_timeout.md b/design/src/rfcs/rfc0031_providing_fallback_credentials_on_timeout.md index 98ccfad3d9..f6e76e87a8 100644 --- a/design/src/rfcs/rfc0031_providing_fallback_credentials_on_timeout.md +++ b/design/src/rfcs/rfc0031_providing_fallback_credentials_on_timeout.md @@ -27,14 +27,14 @@ We have mentioned static stability. Supporting it calls for the following functi - REQ 1: Once a credentials provider has served credentials, it should continue serving them in the event of a timeout (whether internal or external) while obtaining refreshed credentials. Today, we have the following trait method to obtain credentials: -```rust +```rust,ignore fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> where Self: 'a, ``` This method returns a future, which can be raced against a timeout future as demonstrated by the following code snippet from `LazyCredentialsCache`: -```rust +```rust,ignore let timeout_future = self.sleeper.sleep(self.load_timeout); // by default self.load_timeout is 5 seconds. // --snip-- let future = Timeout::new(provider.provide_credentials(), timeout_future); @@ -76,7 +76,7 @@ Proposal To address the problem in the previous section, we propose to add a new method to the `ProvideCredentials` trait called `fallback_on_interrupt`. This method allows credentials providers to have a fallback mechanism on an external timeout and to serve credentials to users if needed. There are two options as to how it is implemented, either as a synchronous primitive or as an asynchronous primitive. #### Option A: Synchronous primitive -```rust +```rust,ignore pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { // --snip-- @@ -90,7 +90,7 @@ pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { - :-1: It may turn into a blocking operation if it takes longer than it should. #### Option B: Asynchronous primitive -```rust +```rust,ignore mod future { // --snip-- @@ -117,7 +117,7 @@ pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { Option A cannot be reversible in the future if we are to support the use case for asynchronously retrieving the fallback credentials, whereas option B allows us to continue supporting both ready and pending futures when retrieving the fallback credentials. However, `fallback_on_interrupt` is supposed to return credentials that have been set aside in case `provide_credentials` is timed out. To express that intent, we choose option A and document that users should NOT go fetch new credentials in `fallback_on_interrupt`. The user experience for the code snippet in question will look like this once this proposal is implemented: -```rust +```rust,ignore let timeout_future = self.sleeper.sleep(self.load_timeout); // by default self.load_timeout is 5 seconds. // --snip-- let future = Timeout::new(provider.provide_credentials(), timeout_future); @@ -146,7 +146,7 @@ Almost all credentials providers do not have to implement their own `fallback_on Considering the two cases we analyzed above, implementing `CredentialsProviderChain::fallback_on_interrupt` is not so straightforward. Keeping track of whose turn in the chain it is to call `provide_credentials` when an external timeout has occurred is a challenging task. Even if we figured it out, that would still not satisfy `Case 2` above, because it was provider 1 that was actively running when the external timeout kicked in, but the chain should return credentials from provider 2, not from provider 1. With that in mind, consider instead the following approach: -```rust +```rust,ignore impl ProvideCredentials for CredentialsProviderChain { // --snip-- @@ -174,7 +174,7 @@ Alternative In this section, we will describe an alternative approach that we ended up dismissing as unworkable. Instead of `fallback_on_interrupt`, we considered the following method to be added to the `ProvideCredentials` trait: -```rust +```rust,ignore pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { // --snip-- @@ -201,7 +201,7 @@ pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { } ``` `provide_credentials_with_timeout` encapsulated the timeout race and allowed users to specify how long the external timeout for `provide_credentials` would be. The code snippet from `LazyCredentialsCache` then looked like -```rust +```rust,ignore let sleeper = Arc::clone(&self.sleeper); let load_timeout = self.load_timeout; // by default self.load_timeout is 5 seconds. // --snip-- @@ -217,7 +217,7 @@ let result = cache // --snip-- ``` However, implementing `CredentialsProviderChain::provide_credentials_with_timeout` quickly ran into the following problem: -```rust +```rust,ignore impl ProvideCredentials for CredentialsProviderChain { // --snip-- @@ -289,7 +289,7 @@ This a case where `CredentialsProviderChain::fallback_on_interrupt` requires the

The outermost chain is a `CredentialsProviderChain` and follows the precedence policy for `fallback_on_interrupt`. It contains a sub-chain that, in turn, contains provider 1 and provider 2. This sub-chain implements its own `fallback_on_interrupt` to realize the recency policy for fallback credentials found in provider 1 and provider 2. Conceptually, we have -``` +```rust,ignore pub struct FallbackRecencyChain { provider_chain: CredentialsProviderChain, } @@ -310,7 +310,7 @@ impl ProvideCredentials for FallbackRecencyChain { } ``` We can then compose the entire chain like so: -``` +```rust,ignore let provider_1 = /* ... */ let provider_2 = /* ... */ let provider_3 = /* ... */ diff --git a/design/src/rfcs/rfc0032_better_constraint_violations.md b/design/src/rfcs/rfc0032_better_constraint_violations.md index d8648df316..6b44d27835 100644 --- a/design/src/rfcs/rfc0032_better_constraint_violations.md +++ b/design/src/rfcs/rfc0032_better_constraint_violations.md @@ -67,7 +67,7 @@ A constrained type has a fallible constructor by virtue of it implementing the [`TryFrom`] trait. The error type this constructor may yield is known as a **constraint violation**: -```rust +```rust,ignore impl TryFrom for ConstrainedType { type Error = ConstraintViolation; @@ -90,7 +90,7 @@ structure A { Yields: -```rust +```rust,ignore /// See [`A`](crate::model::A). pub mod a { #[derive(std::cmp::PartialEq, std::fmt::Debug)] @@ -107,7 +107,7 @@ variant. Constraint violations can occur in application code: -```rust +```rust,ignore use my_server_sdk::model let res = model::a::Builder::default().build(); // We forgot to set `member`. @@ -148,7 +148,7 @@ string LengthString This produces: -```rust +```rust,ignore pub struct LengthMap( pub(crate) std::collections::HashMap, ); @@ -206,7 +206,7 @@ the server framework uses upon deserialization. Observe how `LengthMapOfLengthStringsUnconstrained` is _fully unconstrained_ and how the `try_from` constructor can yield `ConstraintViolation::Value`. -```rust +```rust,ignore pub(crate) mod length_map_of_length_strings_unconstrained { #[derive(Debug, Clone)] pub(crate) struct LengthMapOfLengthStringsUnconstrained( @@ -260,7 +260,7 @@ violation type into two types, which this RFC proposes: 2. one for use by user application code, with `pub` visibility, named `ConstraintViolation`. -```rust +```rust,ignore pub mod length_map { pub enum ConstraintViolation { Length(usize), @@ -362,7 +362,7 @@ string LengthPatternString Yields: -```rust +```rust,ignore pub struct LengthPatternString(pub(crate) std::string::String); impl LengthPatternString { @@ -446,7 +446,7 @@ Let's consider a `ConstraintViolations` type (note the plural) that represents a collection of constraint violations that can occur _within user application code_. Roughly: -```rust +```rust,ignore pub ConstraintViolations(pub(crate) Vec); impl IntoIterator for ConstraintViolations { ... } @@ -558,7 +558,7 @@ string LengthString The corresponding `ConstraintViolationException` Rust type for the `LengthMap` shape is: -```rust +```rust,ignore pub mod length_map { pub enum ConstraintViolation { Length(usize), @@ -575,7 +575,7 @@ pub mod length_map { `ConstraintViolationExceptions` is just a container over this type: -```rust +```rust,ignore pub ConstraintViolationExceptions(pub(crate) Vec); impl IntoIterator for ConstraintViolationExceptions { ... } @@ -604,8 +604,8 @@ string LengthPatternString This would yield: -```rust -pub ConstraintViolations(pub(crate) Vec); +```rust,ignore +pub struct ConstraintViolations(pub(crate) Vec); impl IntoIterator for ConstraintViolations { ... } @@ -656,7 +656,7 @@ string LengthPatternString This would yield, as per the first substitution: -```rust +```rust,ignore pub mod length_pattern_string { pub struct ConstraintViolations { pub length: Option, @@ -693,7 +693,7 @@ map LengthMap { This gives us: -```rust +```rust,ignore pub mod length_map { pub struct ConstraintViolations { pub length: Option, @@ -752,7 +752,7 @@ structure A { And this time let's feature _both_ the resulting `ConstraintViolationExceptions` and `ConstraintViolations` types: -```rust +```rust,ignore pub mod a { pub struct ConstraintViolationExceptions { // All fields must be `Option`, despite the members being `@required`, diff --git a/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md b/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md index 3e78f4070c..9ec673248c 100644 --- a/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md +++ b/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md @@ -156,7 +156,7 @@ Example Interactions ### Generic Handling Case -```rust +```rust,ignore // A re-export of the RequestId trait use aws_sdk_service::primitives::RequestId; @@ -170,7 +170,7 @@ my_request_id_logging_fn(&result); ### Success Case -```rust +```rust,ignore use aws_sdk_service::primitives::RequestId; let output = client.some_operation().send().await?; @@ -179,7 +179,7 @@ println!("request ID: {:?}", output.request_id()); ### Error Case with `SdkError` -```rust +```rust,ignore use aws_sdk_service::primitives::RequestId; match client.some_operation().send().await { @@ -192,7 +192,7 @@ match client.some_operation().send().await { ### Error Case with operation error -```rust +```rust,ignore use aws_sdk_service::primitives::RequestId; match client.some_operation().send().await { diff --git a/design/src/rfcs/rfc0034_smithy_orchestrator.md b/design/src/rfcs/rfc0034_smithy_orchestrator.md index 84d5952253..075a83e33a 100644 --- a/design/src/rfcs/rfc0034_smithy_orchestrator.md +++ b/design/src/rfcs/rfc0034_smithy_orchestrator.md @@ -51,7 +51,7 @@ When a smithy client communicates with a smithy service, messages are handled by For many users, the changes described by this RFC will be invisible. Making a request with an orchestrator-based SDK client looks very similar to the way requests were made pre-RFC: -```rust +```rust,ignore let sdk_config = aws_config::load_from_env().await; let client = aws_sdk_s3::Client::new(&sdk_config); let res = client.get_object() @@ -111,7 +111,7 @@ Interceptors are similar to middlewares, in that they are functions that can rea As mentioned above, interceptors may read/write a context object that is shared between all interceptors: -```rust +```rust,ignore pub struct InterceptorContext { // a.k.a. the input message modeled_request: ModReq, @@ -134,7 +134,7 @@ The optional request and response types in the interceptor context can only be a Imagine we have some sort of request signer. This signer doesn't refer to any orchestrator types. All it needs is a `HeaderMap` along with two strings, and will return a signature in string form. -```rust +```rust,ignore struct Signer; impl Signer { @@ -147,7 +147,7 @@ impl Signer { Now imagine things from the orchestrator's point of view. It requires something that implements an `AuthOrchestrator` which will be responsible for resolving the correct auth scheme, identity, and signer for an operation, as well as signing the request -```rust +```rust,ignore pub trait AuthOrchestrator: Send + Sync + Debug { fn auth_request(&self, req: &mut Req, cfg: &ConfigBag) -> Result<(), BoxError>; } @@ -171,7 +171,7 @@ fn invoke() { The specific implementation of the `AuthOrchestrator` is what brings these two things together: -```rust +```rust,ignore struct Sigv4AuthOrchestrator; impl AuthOrchestrator for Sigv4AuthOrchestrator { @@ -201,7 +201,7 @@ This intermediate code should be free from as much logic as possible. Whenever p > > *See [typemap](https://docs.rs/typemap), [type-map](https://docs.rs/crate/type-map), [http::Extensions](https://docs.rs/http/latest/http/struct.Extensions.html), and [actix_http::Extensions](https://docs.rs/actix-http/latest/actix_http/struct.Extensions.html) for examples.* -```rust +```rust,ignore let conf: ConfigBag = aws_config::from_env() // Configuration can be common to all smithy clients .with(RetryConfig::builder().disable_retries().build()) @@ -229,7 +229,7 @@ Setting configuration that will not be used wastes memory and can make debugging Configuration has precedence. Configuration set on an operation will override configuration set on a client, and configuration set on a client will override default configuration. However, configuration with a higher precedence can also augment configuration with a lower precedence. For example: -```rust +```rust,ignore let conf: ConfigBag = aws_config::from_env() .with( SomeConfig::builder() @@ -269,7 +269,7 @@ Config values are wrapped in a special enum called `Value` with three variants: Builders are defined like this: -```rust +```rust,ignore struct SomeBuilder { value: Value, } @@ -316,7 +316,7 @@ Configuration is resolved in a fixed manner by reading the "lowest level" of con *I've omitted some of the error conversion to shorten this example and make it easier to understand. The real version will be messier.* -```rust +```rust,ignore /// `In`: The input message e.g. `ListObjectsRequest` /// `Req`: The transport request message e.g. `http::Request` /// `Res`: The transport response message e.g. `http::Response` @@ -452,7 +452,7 @@ async fn make_an_attempt( At various points in the execution of `invoke`, trait objects are fetched from the `ConfigBag`. These are preliminary definitions of those traits: -```rust +```rust,ignore pub trait TraceProbe: Send + Sync + Debug { fn dispatch_events(&self, cfg: &ConfigBag) -> BoxFallibleFut<()>; } diff --git a/design/src/rfcs/rfc0035_collection_defaults.md b/design/src/rfcs/rfc0035_collection_defaults.md new file mode 100644 index 0000000000..a1aa389b4e --- /dev/null +++ b/design/src/rfcs/rfc0035_collection_defaults.md @@ -0,0 +1,82 @@ + +RFC: Collection Defaults +============= + + +> Status: RFC +> +> Applies to: client + + +For a summarized list of proposed changes, see the [Changes Checklist](#changes-checklist) section. + + +This RFC proposes a **breaking change** to how generated clients automatically provide default values for collections. Currently the SDK generated fields for `List` generate optional values: +```rust + ///

Container for elements related to a particular part. + pub fn parts(&self) -> Option<&[crate::types::Part]> { + self.parts.as_deref() + } +``` +This is _almost never_ what users want and leads to code noise when using collections: +```rust +async fn get_builds() { + let project = codebuild + .list_builds_for_project() + .project_name(build_project) + .send() + .await?; + let build_ids = project + .ids() + .unwrap_or_default(); + // ^^^^^^^^^^^^^^^^^^ this is pure noise +} +``` + +This RFC proposes unwrapping into default values in our accessor methods. + + +Terminology +----------- + +- **Accessor**: The Rust SDK defines accessor methods on modeled structures for fields to make them more convenient for users +- **Struct field**: The accessors point to concrete fields on the struct itself. + + +The user experience if this RFC is implemented +---------------------------------------------- + +In the current version of the SDK, users must call `.unwrap_or_default()` frequently. +Once this RFC is implemented, users will be able to use these accessors directly. In the rare case where users need to +distinguish be `None` and `[]`, we will direct users towards `model..is_some()`. + +```rust +async fn get_builds() { + let project = codebuild + .list_builds_for_project() + .project_name(build_project) + .send() + .await?; + let build_ids = project.ids(); + // Goodbye to this line: + // .unwrap_or_default(); +} +``` + + +How to actually implement this RFC +---------------------------------- + +In order to implement this feature, we need update the code generate accessors for lists and maps to add `.unwrap_or_default()`. Because we are returning slices `unwrap_or_default()` does not produce any additional allocations for empty collection. + +### Could this be implemented for `HashMap`? +This works for lists because we are returning a slice (allowing a statically owned `&[]` to be returned.) If we want to support HashMaps in the future this _is_ possible by using `OnceCell` to create empty HashMaps for requisite types. This would allow us to return references to those empty maps. + +### Isn't this handled by the default trait? +No, many existing APIs don't have the default trait. + +Changes checklist +----------------- +Estimated total work: 2 days +- [ ] Update accessor method generation to auto flatten lists +- [ ] Update docs for accessors to guide users to `.field.is_some()` if they MUST determine if the field was set. diff --git a/design/src/rfcs/rfc0036_http_dep_elimination.md b/design/src/rfcs/rfc0036_http_dep_elimination.md new file mode 100644 index 0000000000..355d0f92dd --- /dev/null +++ b/design/src/rfcs/rfc0036_http_dep_elimination.md @@ -0,0 +1,108 @@ + +RFC: Eliminating Public `http` dependencies +============= + +> Status: RFC +> +> Applies to: client + + +For a summarized list of proposed changes, see the [Changes Checklist](#changes-checklist) section. + + +This RFC defines how we plan to refactor the SDK to allow the SDK to consume a `1.0` version of `hyper`, `http-body`, +and `http` at a later date. Currently, `hyper` is `0.14.x` and a `1.0` release candidate series is in progress. However, +there are [open questions](https://github.com/orgs/hyperium/projects/1/views/4) that may significantly delay the launch +of +these three crates. We do not want to tie the `1.0` of the Rust SDK to these crates. + + +Terminology +----------- + +- **http-body**: A crate (and trait) defining how HTTP bodies work. Notably, the change from `0.*` to `1.0` changes `http-body` + to operate on frames instead of having separate methods. +- `http` (crate): a low level crate of `http` primitives (no logic, just requests and responses) +- ossified dependency: An ossified dependency describes a dependency that, when a new version is released, cannot be utilized without breaking changes. For example, if the `mutate_request` function on every operation operates on `&mut http::Request` where `http = 0.2`, that dependency is "ossified." Compare this to a function that offers the ability to convert something into an `http = 0.2` request—since http=1 and http=0.2 are largely equivalent, the + existence of this function does not prevent us from using http = 1 in the future. In general terms, **functions that operate on references are much more likely to ossify—There is no practical way for someone to mutate an `http = 0.2` request if you have an `http = 1` request other than a time-consuming clone, and reconversion process. + + + +## Why is this important? + +**Performance**: +At some point in the Future, `hyper = 1`, `http = 1` and `http-body = 1` will be released. It takes ~1-2 microseconds to rebuild an HTTP request. If we assume that `hyper = 1` will only operate on `http = 1` requests, then if we can't use `http = 1` requests internally, our only way of supporting `hyper = 1` will be to convert the HTTP request at dispatch time. Besides pinning us to a potentially unsupported version of the HTTP crate, this will prevent us from directly dispatching requests in an efficient manner. With a total overhead of 20µs for the SDK, 1µs is not insignificant. Furthermore, it grows as the number of request headers grow. A benchmark should be run for a realistic HTTP request e.g. one that we send to S3. + +**Hyper Upgrade**: +Hyper 1 is significantly more flexible than Hyper 0.14.x, especially WRT to connection management & pooling. If we don't make these changes, the upgrade to Hyper 1.x could be significantly more challenging. + +**Security Fixes**: +If we're still on `http = 0.*` and a vulnerability is identified, we may end up needing to manually contribute the patch. The `http` crate is not trivial and contains parsing logic and optimized code (including a non-trivial amount of `unsafe`). [See this GitHub issue](https://github.com/hyperium/http/issues/412). Notable is that one issue may be unsound and result in changing the public API. + +**API Friendliness** +If we ship with an API that public exposes customers to `http = 0.*`, we have the API forever. We have to consider that we aren't shipping the Rust SDK for this month or even this year but probably the Rust SDK for the next 5-10 years. + +The user experience if this RFC is implemented +---------------------------------------------- +Customers are impacted in 3 main locations: +1. HTTP types in Interceptors +2. HTTP types in `customize(...)` +3. HTTP types in Connectors + +In all three of these cases, users would interact with our `http` wrapper types instead. + + +In the current version of the SDK, we expose public dependencies on the `http` crate in several key places: + +1. The `sigv4` crate. The `sigv4` crate currently operates directly on many types from the `http` crate. This is unnecessary and actually makes the crate more difficult to use. Although `http` may be used internally, `http` will be removed from the public API of this crate. +2. Interceptor Context: `interceptor`s can mutate the HTTP request through an unshielded interface. This requires creating a [wrapper layer around `http::Request`](#http-request-wrapper) and updating already written interceptors. +3. `aws-config`: `http::Response` and `uri` +4. A long tail of exposed requests and responses in the runtime crates. Many of these crates will be removed post-orchestrator so this can be temporarily delayed. + + +How to actually implement this RFC +---------------------------------- + +### Enabling API evolution +One key mechanism that we SHOULD use for allowing our APIs to evolve in the future is usage of `~` version bounds for the runtime crates after releasing 1.0. + +### Http Request Wrapper + +In order to enable HTTP evolution, we will create a set of wrapper structures around `http::Request` and `http::Response`. These will use `http = 0` internally. Since the HTTP crate itself is quite small, including private dependencies on both versions of the crate is a workable solution. In general, we will aim for an API that is close to drop-in compatible to the HTTP crate while ensuring that a different crate could be used as the backing storage. + +```rust +// since it's our type, we can default `SdkBody` +pub struct Request { + // this uses the http = 0.2 request. In the future, we can make an internal enum to allow storing an http = 1 + http_0: http::Request +} +``` + +**Conversion to/from `http::Request`** +One key property here is that although converting to/from an `http::Request` **can** be expensive, this is *not* ossification of the API. This is because the API can support converting from/to both `http = 0` and `http = 1` in the future—because it offers mutation of the request via a unified interface, the request would only need to be converted once for dispatch if there was a mismatch (instead of repeatedly). At some point in the future, the `http = 0` representation could be deprecated and removed or feature gated. + +**Challenges** +1. Creating an HTTP API which is forwards compatible, idiomatic and "truthful" without relying on existing types from Hyper—e.g. when adding a header, we need to account for the possibility that a header is invalid. +2. Allow for future forwards-compatible evolution in the API—A lot of thought went into the `http` crate API w.r.t method parameters, types, and generics. Although we can aim for a simpler solution in some cases (e.g. accepting `&str` instead of `HeaderName`), we need to be careful that we do so while allowing API evolution. + +### Removing the SigV4 HTTP dependency +The SigV4 crate signs a number of `HTTP` types directly. We should change it to accept strings, and when appropriate, iterators of strings for headers. + +### Removing the HTTP dependency from generated clients +Generated clients currently include a public HTTP dependency in `customize`. This should be changed to accept our `HTTP` wrapper type instead or be restricted to a subset of operations (e.g. `add_header`) while forcing users to add an interceptor if they need full control. + + +In order to implement this feature, we need to add X and update Y... + + +Changes checklist +----------------- + +- [ ] Create the `http::Request` wrapper. Carefully audit for compatibility without breaking changes. 5 Days. +- [ ] Refactor currently written interceptors to use the wrapper: 2 days. +- [ ] Refactor the SigV4 crate to remove the HTTP dependency from the public interface: 2 days. +- [ ] Add / validate support for SdkBody `http-body = 1.0rc.2` either in a PR or behind a feature gate. Test this to + ensure it works with Hyper. +- [ ] Remove `http::Response` and `Uri` from the public exposed types in `aws-config`: 1-4 days. +- [ ] Long tail of other usages: 1 week +- [ ] Implement `~` versions for SDK Crate => runtime crate dependencies: 1 week diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index 51f7b68f2d..d15f4fbcc0 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -42,35 +42,37 @@ service PokemonService { Smithy Rust will use this model to produce the following API: ```rust +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use pokemon_service_server_sdk::{input::*, output::*, error::*, operation_shape::*, PokemonService}; // A handler for the `GetPokemonSpecies` operation (the `PokemonSpecies` resource). async fn get_pokemon_species(input: GetPokemonSpeciesInput) -> Result { - /* implementation */ + todo!() } -// Apply a `tower::Layer` to a handler. -let get_pokemon_species_op = GetPokemonSpecies::from_handler(get_pokemon_species).layer(/* some `tower::Layer` */); - // Use the service builder to create `PokemonService`. let pokemon_service = PokemonService::builder_without_plugins() // Pass the handler directly to the service builder... .get_pokemon_species(get_pokemon_species) - // ...or pass the layered handler. - .get_pokemon_species_operation(get_pokemon_species_op) /* other operation setters */ .build() + # ; Result::<(), ()>::Ok(()) .expect("failed to create an instance of the Pokémon service"); +# let pokemon_service: Result, _> = pokemon_service; ``` ## Operations A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize a Smithy Operation as syntax for specifying a function type. -We represent this in Rust using the [`OperationShape`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L8-L22) trait: +We represent this in Rust using the [`OperationShape`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/trait.OperationShape.html) trait: ```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::shape_id::ShapeId; pub trait OperationShape { /// The name of the operation. - const NAME: &'static str; + const ID: ShapeId; /// The operation input. type Input; @@ -80,6 +82,13 @@ pub trait OperationShape { /// exists. type Error; } +# use aws_smithy_http_server::operation::OperationShape as OpS; +# impl OperationShape for T { +# const ID: ShapeId = ::ID; +# type Input = ::Input; +# type Output = ::Output; +# type Error = ::Error; +# } ``` For each Smithy Operation shape, @@ -98,11 +107,15 @@ operation GetPokemonSpecies { the following implementation is generated ```rust +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::{operation::OperationShape, shape_id::ShapeId}; +# use pokemon_service_server_sdk::{input::*, output::*, error::*}; /// Retrieve information about a Pokémon species. pub struct GetPokemonSpecies; impl OperationShape for GetPokemonSpecies { - const NAME: &'static str = "com.aws.example#GetPokemonSpecies"; + const ID: ShapeId = ShapeId::new("com.aws.example#GetPokemonSpecies", "com.aws.example", "GetPokemonSpecies"); type Input = GetPokemonSpeciesInput; type Output = GetPokemonSpeciesOutput; @@ -116,46 +129,35 @@ Note that the `GetPokemonSpecies` marker structure is a zero-sized type (ZST), a The following nomenclature will aid us in our survey. We describe a `tower::Service` as a "model service" if its request and response are Smithy structures, as defined by the `OperationShape` trait - the `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput`, and `GetPokemonSpeciesError` described above. Similarly, we describe a `tower::Service` as a "HTTP service" if its request and response are [`http`](https://github.com/hyperium/http) structures - `http::Request` and `http::Response`. -In contrast to the marker ZSTs above, the [`Operation`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L192-L198) structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. - -```rust -/// A Smithy operation, represented by a [`Service`](tower::Service) `S` and a [`Layer`](tower::Layer) `L`. -/// -/// The `L` is held and applied lazily during [`Upgradable::upgrade`]. -pub struct Operation { - inner: S, - layer: L, -} -``` - -The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely [`OperationShapeExt`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L24-L45): +The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely [`OperationShapeExt`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/trait.OperationShapeExt.html): ```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::operation::*; /// An extension trait over [`OperationShape`]. pub trait OperationShapeExt: OperationShape { - /// Creates a new [`Operation`] for well-formed [`Handler`]s. - fn from_handler(handler: H) -> Operation> + /// Creates a new [`Service`] for well-formed [`Handler`]s. + fn from_handler(handler: H) -> IntoService where - H: Handler, - Self: Sized, - { - Operation::from_handler(handler) - } + H: Handler, + Self: Sized; - /// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s. - fn from_service(svc: S) -> Operation> + /// Creates a new [`Service`] for well-formed [`Service`](tower::Service)s. + fn from_service(svc: S) -> Normalize where - S: OperationService, - Self: Sized, - { - Operation::from_service(svc) - } + S: OperationService, + Self: Sized; } +# use aws_smithy_http_server::operation::OperationShapeExt as OpS; +# impl OperationShapeExt for T { +# fn from_handler(handler: H) -> IntoService where H: Handler, Self: Sized { ::from_handler(handler) } +# fn from_service(svc: S) -> Normalize where S: OperationService, Self: Sized { ::from_service(svc) } +# } ``` Observe that there are two constructors provided: `from_handler` which takes a `H: Handler` and `from_service` which takes a `S: OperationService`. In both cases `Self` is passed as a parameter to the traits - this constrains `handler: H` and `svc: S` to the signature given by the implementation of `OperationShape` on `Self`. -The [`Handler`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/handler.rs#L21-L29) and [`OperationService`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs#L15-L29) both serve a similar purpose - they provide a common interface for converting to a model service `S`. +The [`Handler`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/trait.Handler.html) and [`OperationService`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/trait.OperationService.html) both serve a similar purpose - they provide a common interface for converting to a model service `S`. - The `Handler` trait covers all async functions taking `GetPokemonSpeciesInput` and asynchronously returning a `Result`. - The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput`. @@ -163,72 +165,81 @@ The [`Handler`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693 The `from_handler` constructor is used in the following way: ```rust -async fn get_pokemon_service(input: GetPokemonServiceInput) -> Result { - /* Handler logic */ +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +use pokemon_service_server_sdk::{ + input::GetPokemonSpeciesInput, + output::GetPokemonSpeciesOutput, + error::GetPokemonSpeciesError, + operation_shape::GetPokemonSpecies +}; +use aws_smithy_http_server::operation::OperationShapeExt; + +async fn get_pokemon_service(input: GetPokemonSpeciesInput) -> Result { + todo!() } -let operation = GetPokemonService::from_handler(get_pokemon_service); +let operation = GetPokemonSpecies::from_handler(get_pokemon_service); ``` Alternatively, `from_service` constructor: ```rust -struct Svc { - /* ... */ -} - -impl Service for Svc { - type Response = GetPokemonServiceOutput; - type Error = GetPokemonServiceError; +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# extern crate tower; +use pokemon_service_server_sdk::{ + input::GetPokemonSpeciesInput, + output::GetPokemonSpeciesOutput, + error::GetPokemonSpeciesError, + operation_shape::GetPokemonSpecies +}; +use aws_smithy_http_server::operation::OperationShapeExt; +use std::task::{Context, Poll}; +use tower::Service; +struct Svc { /* ... */ } -let svc: Svc = /* ... */; -let operation = GetPokemonService::from_service(svc); -``` - -To summarize, the `S`, in `Operation`, is a _model service_ constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. More detailed information on these conversions is provided in the [Handler and OperationService section](https://github.com/awslabs/smithy-rs/blob/39c0096c33417d44f125a042c112b3c16918098a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L50-L100) Rust docs. +impl Service for Svc { + type Response = GetPokemonSpeciesOutput; + type Error = GetPokemonSpeciesError; + type Future = /* Future> */ + # std::future::Ready>; -Now, what about the `L` in `Operation`? The `L` is a [`tower::Layer`](https://docs.rs/tower/latest/tower/layer/trait.Layer.html), or colloquially "middleware", that is applied to a _HTTP service_. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + todo!() + } -```rust -impl Operation { - /// Applies a [`Layer`] to the operation _after_ it has been upgraded via [`Operation::upgrade`]. - pub fn layer(self, layer: NewL) -> Operation> { - Operation { - inner: self.inner, - layer: Stack::new(self.layer, layer), - } + fn call(&mut self, input: GetPokemonSpeciesInput) -> Self::Future { + todo!() } } -``` - -where [`tower::layer::util::Stack`](https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html) is used to chain layers together. - -A typical use of this might be: -```rust -let operation = GetPokemonSpecies::from_handler(handler).layer(RequestBodyLimitLayer::new(500)); +let svc: Svc = Svc { /* ... */ }; +let operation = GetPokemonSpecies::from_service(svc); ``` -where [`RequestBodyLimitLayer`](https://docs.rs/tower-http/latest/tower_http/limit/struct.RequestBodyLimitLayer.html) limits the size of the HTTP request body to the `GetPokemonSpecies` operation. - -As mentioned, `L` is applied _after_ the `Operation` has been "upgraded" to a HTTP service. The procedure of upgrading a model service to a HTTP service is described in the [Upgrading a Model Service](#upgrading-a-model-service) section below. +To summarize a _model service_ constructed can be constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. More detailed information on these conversions is provided in the [Handler and OperationService section](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/index.html) Rust docs. ## Serialization and Deserialization -A [Smithy protocol](https://awslabs.github.io/smithy/2.0/spec/protocol-traits.html#serialization-and-protocol-traits) specifies the serialization/deserialization scheme - how a HTTP request is transformed into a modelled input and a modelled output to a HTTP response. The is formalized using the [`FromRequest`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/request.rs#L156-L164) and [`IntoResponse`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/response.rs#L40-L44) traits: +A [Smithy protocol](https://awslabs.github.io/smithy/2.0/spec/protocol-traits.html#serialization-and-protocol-traits) specifies the serialization/deserialization scheme - how a HTTP request is transformed into a modelled input and a modelled output to a HTTP response. The is formalized using the [`FromRequest`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/request/trait.FromRequest.html) and [`IntoResponse`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/response.rs#L40-L44) traits: ```rust +# extern crate aws_smithy_http_server; +# extern crate http; +# use aws_smithy_http_server::body::BoxBody; +# use std::future::Future; /// Provides a protocol aware extraction from a [`Request`]. This consumes the /// [`Request`], in contrast to [`FromParts`]. -pub trait FromRequest: Sized { +pub trait FromRequest: Sized { type Rejection: IntoResponse; type Future: Future>; /// Extracts `self` from a [`Request`] asynchronously. - fn from_request(request: http::Request) -> Self::Future; + fn from_request(request: http::Request) -> Self::Future; } /// A protocol aware function taking `self` to [`http::Response`]. @@ -236,11 +247,30 @@ pub trait IntoResponse { /// Performs a conversion into a [`http::Response`]. fn into_response(self) -> http::Response; } +# use aws_smithy_http_server::request::FromRequest as FR; +# impl> FromRequest for T { +# type Rejection = >::Rejection; +# type Future = >::Future; +# fn from_request(request: http::Request) -> Self::Future { +# >::from_request(request) +# } +# } +# use aws_smithy_http_server::response::IntoResponse as IR; +# impl> IntoResponse

for T { +# fn into_response(self) -> http::Response { >::into_response(self) } +# } ``` Note that both traits are parameterized by `Protocol`. These [protocols](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) exist as ZST marker structs: ```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::protocol::{ +# aws_json_10::AwsJson1_0 as _, +# aws_json_11::AwsJson1_1 as _, +# rest_json_1::RestJson1 as _, +# rest_xml::RestXml as _, +# }; /// [AWS REST JSON 1.0 Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html). pub struct RestJson1; @@ -272,23 +302,16 @@ stateDiagram-v2 into_response --> [*]: HTTP Response ``` -This is formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/9a6de1f533f8743dbbc3fa6ad974d104c8b841f4/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L74-L82) HTTP service. The `tower::Service` implementation is approximately: +This is formalized by the [`Upgrade`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/struct.Upgrade.html) HTTP service. The `tower::Service` implementation is approximately: -```rust +```rust,ignore impl Service for Upgrade where - // `Op` is used to specify the operation shape - Op: OperationShape, - // Smithy input must convert from a HTTP request - Op::Input: FromRequest

, - // Smithy output must convert into a HTTP response - Op::Output: IntoResponse

, - // Smithy error must convert into a HTTP response - OpError: IntoResponse

, - - // The signature of the inner service is correct - S: Service, - + Input: FromRequest, + S: Service, + S::Response: IntoResponse

, + S::Error: IntoResponse

, +{ async fn call(&mut self, request: http::Request) -> http::Response { let model_request = match ::from_request(request).await { Ok(ok) => ok, @@ -297,75 +320,50 @@ where let model_response = self.model_service.call(model_request).await; model_response.into_response() } +} ``` -When we `GetPokemonService::from_handler` or `GetPokemonService::from_service`, the model service produced, `S`, will meet the constraints above. +When we `GetPokemonSpecies::from_handler` or `GetPokemonSpecies::from_service`, the model service produced, `S`, will meet the constraints above. -There is an associated `Layer`, `UpgradeLayer` which constructs `Upgrade` from a service. +There is an associated `Plugin`, `UpgradePlugin` which constructs `Upgrade` from a service. The upgrade procedure is finalized by the application of the `Layer` `L`, referenced in `Operation`. In this way the entire upgrade procedure takes an `Operation` and returns a HTTP service. ```mermaid stateDiagram-v2 direction LR - [*] --> S: HTTP Request - state L { - state Upgrade { - S + [*] --> UpgradePlugin: HTTP Request + state HttpPlugin { + state UpgradePlugin { + direction LR + [*] --> S: Model Input + S --> [*] : Model Output + state ModelPlugin { + S + } } } - S --> [*]: HTTP Response + UpgradePlugin --> [*]: HTTP Response ``` -Note that the `S` and `L` are specified by logic written, in Rust, by the customer, whereas `Upgrade`/`UpgradeLayer` is specified entirely by Smithy model via the protocol, [HTTP bindings](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html), etc. - -The procedure of taking a struct and transforming it into a HTTP service is formalized by the [`Upgradable`](https://github.com/awslabs/smithy-rs/blob/9a6de1f533f8743dbbc3fa6ad974d104c8b841f4/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L220-L225) trait: - -```rust -/// An interface to convert a representation of a Smithy operation into a [`Route`]. -pub trait Upgradable { - /// Upgrade the representation of a Smithy operation to a [`Route`]. - fn upgrade(self, plugin: &Plugin) -> Route; -} -``` - -Why do we need a trait for this? Why not simply write an `upgrade` method on `Operation`? The reason is that we might _not_ want to supply an `Operation` to the service builder, instead we might want to supply something that overrides the typical upgrade procedure. - -Below we give an example of a ZST which can be provided to the builder, which also satisfies `Upgradable` and returns a `MissingFailure` `tower::Service`. This `MissingFailure` service simply returns a status code 500. - -```rust -/// A marker struct indicating an [`Operation`] has not been set in a builder. -/// -/// This _does_ implement [`Upgradable`] but produces a [`Service`] which always returns an internal failure message. -pub struct FailOnMissingOperation; - -impl Upgradable for FailOnMissingOperation -where - InternalFailureException: IntoResponse, - Protocol: 'static, -{ - fn upgrade(self, _plugin: &Plugin) -> Route { - Route::new(MissingFailure { _protocol: PhantomData }) - } -} -``` - -We go into more detail on how the `Upgradable` trait is used in conjunction with builders in the [Builders](#builders) section below. +Note that the `S` is specified by logic written, in Rust, by the customer, whereas `UpgradePlugin` is specified entirely by Smithy model via the protocol, [HTTP bindings](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html), etc. ## Routers Different protocols supported by Smithy enjoy different routing mechanisms, for example, [AWS JSON 1.0](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-json-1_0-protocol.html#protocol-behaviors) uses the `X-Amz-Target` header to select an operation, whereas [AWS REST XML](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restxml-protocol.html) uses the [HTTP label trait](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html#httplabel-trait). -Despite their differences, all routing mechanisms satisfy a common interface. This is formalized using the `Router` trait: +Despite their differences, all routing mechanisms satisfy a common interface. This is formalized using the [Router](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/routing/trait.Router.html) trait: ```rust +# extern crate aws_smithy_http_server; +# extern crate http; /// An interface for retrieving an inner [`Service`] given a [`http::Request`]. -pub trait Router { +pub trait Router { type Service; type Error; /// Matches a [`http::Request`] to a target [`Service`]. - fn match_route(&self, request: &http::Request) -> Result; + fn match_route(&self, request: &http::Request) -> Result; } ``` @@ -373,7 +371,7 @@ which provides the ability to determine an inner HTTP service from a collection Types which implement the `Router` trait are converted to a HTTP service via the `RoutingService` struct: -```rust +```rust,ignore /// A [`Service`] using a [`Router`] `R` to redirect messages to specific routes. /// /// The `Protocol` parameter is used to determine the serialization of errors. @@ -423,6 +421,90 @@ state in <> ServiceC --> [*] ``` +## Plugins + + +A [`Plugin`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/plugin/trait.Plugin.html) is a +[`tower::Layer`] with two extra type parameters, `Service` and `Operation`, corresponding to [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) and [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation). This allows the middleware to be +parameterized them and change behavior depending on the context in which it's applied. + +```rust +# extern crate aws_smithy_http_server; +pub trait Plugin { + type Output; + + fn apply(&self, input: T) -> Self::Output; +} +# use aws_smithy_http_server::plugin::Plugin as Pl; +# impl> Plugin for U { +# type Output = >::Output; +# fn apply(&self, input: T) -> Self::Output { >::apply(self, input) } +# } +``` + +An example `Plugin` implementation can be found in [/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs). + +Plugins can be applied in two places: + +- HTTP plugins, which are applied pre-deserialization/post-serialization, acting on HTTP requests/responses. +- Model plugins, which are applied post-deserialization/pre-serialization, acting on model inputs/outputs/errors. + +```mermaid +stateDiagram-v2 + direction LR + [*] --> S: HTTP Request + state HttpPlugin { + state UpgradePlugin { + state ModelPlugin { + S + } + } + } + S --> [*]: HTTP Response +``` + +The service builder API requires plugins to be specified upfront - they must be passed as an argument to `builder_with_plugins` and cannot be modified afterwards. + +You might find yourself wanting to apply _multiple_ plugins to your service. +This can be accommodated via [`HttpPlugins`] and [`ModelPlugins`]. + +```rust +# extern crate aws_smithy_http_server; +use aws_smithy_http_server::plugin::HttpPlugins; +# use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; +# use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; + +let http_plugins = HttpPlugins::new().push(LoggingPlugin).push(MetricsPlugin); +``` + +The plugins' runtime logic is executed in registration order. +In the example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. + +If you are vending a plugin, you can leverage `HttpPlugins` or `ModelPlugins` as an extension point: you can add custom methods to it using an extension trait. +For example: + +```rust +# extern crate aws_smithy_http_server; +use aws_smithy_http_server::plugin::{HttpPlugins, PluginStack}; +# use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; +# use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; + +pub trait AuthPluginExt { + fn with_auth(self) -> HttpPlugins>; +} + +impl AuthPluginExt for HttpPlugins { + fn with_auth(self) -> HttpPlugins> { + self.push(AuthPlugin) + } +} + +let http_plugins = HttpPlugins::new() + .push(LoggingPlugin) + // Our custom method! + .with_auth(); +``` + ## Builders The service builder is the primary public API, generated for every [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html). @@ -430,56 +512,88 @@ At a high-level, the service builder takes as input a function for each Smithy O You can create an instance of a service builder by calling either `builder_without_plugins` or `builder_with_plugins` on the corresponding service struct. -> Plugins? What plugins? Don't worry, they'll be covered in a [dedicated section](#plugins) later on! - ```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::routing::Route; /// The service builder for [`PokemonService`]. /// /// Constructed via [`PokemonService::builder`]. -pub struct PokemonServiceBuilder { +pub struct PokemonServiceBuilder { capture_pokemon_operation: Option>, empty_operation: Option>, get_pokemon_species: Option>, get_server_statistics: Option>, get_storage: Option>, health_check_operation: Option>, - plugin: Plugin + http_plugin: HttpPl, + model_plugin: ModelPl, } ``` The builder has two setter methods for each [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) in the [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service): -```rust - /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. - /// - /// This should be an async function satisfying the [`Handler`](aws_smithy_http_server::operation::Handler) trait. - /// See the [operation module documentation](aws_smithy_http_server::operation) for more information. - pub fn get_pokemon_species( - self, - handler: HandlerType, - ) -> Self +```rust,ignore + pub fn get_pokemon_species(self, handler: HandlerType) -> Self where - HandlerType: Handler, - Operation>: - Upgradable, + HandlerType:Handler, + + ModelPl: Plugin< + PokemonService, + GetPokemonSpecies, + IntoService + >, + UpgradePlugin::: Plugin< + PokemonService, + GetPokemonSpecies, + ModelPlugin::Output + >, + HttpPl: Plugin< + PokemonService, + GetPokemonSpecies, + UpgradePlugin::::Output + >, { - self.get_pokemon_species_operation(GetPokemonSpecies::from_handler(handler)) + let svc = GetPokemonSpecies::from_handler(handler); + let svc = self.model_plugin.apply(svc); + let svc = UpgradePlugin::::new() + .apply(svc); + let svc = self.http_plugin.apply(svc); + self.get_pokemon_species_custom(svc) } - /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. - /// - /// This should be an [`Operation`](aws_smithy_http_server::operation::Operation) created from - /// [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) using either - /// [`OperationShape::from_handler`](aws_smithy_http_server::operation::OperationShapeExt::from_handler) or - /// [`OperationShape::from_service`](aws_smithy_http_server::operation::OperationShapeExt::from_service). - pub fn get_pokemon_species_operation( - self, - operation: Operation, - ) -> Self + pub fn get_pokemon_species_service(self, service: S) -> Self where - Operation: Upgradable, + S: OperationService, + + ModelPl: Plugin< + PokemonService, + GetPokemonSpecies, + Normalize + >, + UpgradePlugin::: Plugin< + PokemonService, + GetPokemonSpecies, + ModelPlugin::Output + >, + HttpPl: Plugin< + PokemonService, + GetPokemonSpecies, + UpgradePlugin::::Output + >, { - self.get_pokemon_species = Some(operation.upgrade(&self.plugin)) + let svc = GetPokemonSpecies::from_service(service); + let svc = self.model_plugin.apply(svc); + let svc = UpgradePlugin::::new().apply(svc); + let svc = self.http_plugin.apply(svc); + self.get_pokemon_species_custom(svc) + } + + pub fn get_pokemon_species_custom(mut self, svc: S) -> Self + where + S: Service, Response = Response, Error = Infallible>, + { + self.get_pokemon_species = Some(Route::new(svc)); + self } ``` @@ -500,6 +614,8 @@ Both builder methods take care of: The final outcome, an instance of `PokemonService`, looks roughly like this: ```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::{routing::RoutingService, protocol::rest_json_1::{router::RestRouter, RestJson1}}; /// The Pokémon Service allows you to retrieve information about Pokémon species. #[derive(Clone)] pub struct PokemonService { @@ -518,19 +634,22 @@ stateDiagram-v2 state "..." as C4 direction LR [*] --> in : HTTP Request - UpgradeLayer --> [*]: HTTP Response + UpgradePlugin --> [*]: HTTP Response state PokemonService { state RoutingService { - in --> UpgradeLayer: HTTP Request + in --> UpgradePlugin: HTTP Request in --> C2: HTTP Request in --> C3: HTTP Request in --> C4: HTTP Request state C1 { - state L { - state UpgradeLayer { + state HttpPlugin { + state UpgradePlugin { direction LR [*] --> S: Model Input S --> [*] : Model Output + state ModelPlugin { + S + } } } } @@ -545,159 +664,15 @@ stateDiagram-v2 C4 --> [*]: HTTP Response ``` -## Plugins - - -There are a variety of places in which the customer can apply middleware. During the build: - -- For a specific operation, for example `GetPokemonSpecies`, the model service can be wrapped by a `Layer` before passing it to `GetPokemonSpecies::from_service` constructor. -- The `Operation::layer` method can be used to apply a `Layer` to a specific operation _after_ it's been upgraded. - -After the build is finalized: - -- The entire `PokemonService` HTTP service can be wrapped by a `Layer`. -- Every `Route` in the `Router` can be wrapped by a `Layer` using `PokemonService::layer`. - -Although this provides a reasonably "complete" API, it can be cumbersome in some use cases. Suppose a customer wants to log the operation name when a request is routed to said operation. Writing a `Layer`, `NameLogger`, to log an operation name is simple, however with the current API the customer is forced to do the following - -```rust -let get_pokemon_species = GetPokemonSpecies::from_handler(/* handler */).layer(NameLogger::new("GetPokemonSpecies")); -let get_storage = GetStorage::from_handler(/* handler */).layer(NameLogger::new("GetStorage")); -let do_nothing = DoNothing::from_handler(/* handler */).layer(NameLogger::new("DoNothing")); -/* Repeat for every route... */ -``` - -Note that `PokemonService::layer` cannot be used here because it applies a _single_ layer uniformly across all `Route`s stored in the `Router`. - -```rust -impl PokemonService { - /// Applies a [`Layer`](tower::Layer) uniformly to all routes. - pub fn layer(self, layer: &L) -> PokemonService - where - L: Layer, - { - PokemonService { - router: self.router.map(|s| s.layer(layer)), - } - } -} -``` - -The plugin system solves the general problem of modifying `Operation` prior to the upgrade procedure in a way parameterized by the protocol and operation marker structures. This parameterization removes the excessive boilerplate above. - -The central trait is [`Plugin`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L31-L41): - -```rust -/// A mapping from one [`Operation`] to another. Used to modify the behavior of -/// [`Upgradable`](crate::operation::Upgradable) and therefore the resulting service builder. -/// -/// The generics `Protocol` and `Op` allow the behavior to be parameterized. -pub trait Plugin { - type Service; - type Layer; - - /// Maps an [`Operation`] to another. - fn map(&self, input: Operation) -> Operation; -} -``` - -The `Upgradable::upgrade` method on `Operation`, previously presented in [Upgrading a Model Service](#upgrading-a-model-service), is more accurately: - -```rust - /// Takes the [`Operation`](Operation), applies [`Plugin`], then applies [`UpgradeLayer`] to - /// the modified `S`, then finally applies the modified `L`. - /// - /// The composition is made explicit in the method constraints and return type. - fn upgrade(self, plugin: &Pl) -> Route { - let mapped = plugin.map(self); - let layer = Stack::new(UpgradeLayer::new(), mapped.layer); - Route::new(layer.layer(mapped.inner)) - } -``` - -```mermaid -stateDiagram-v2 - direction TB - Op1: Operation#60;S1, L1#62; - state Op1 { - direction LR - [*] --> S1 : HTTP Request - S1 --> [*]: HTTP Response - state L1 { - Upgrade1 : Upgrade - state Upgrade1 { - S1 - } - } - - } - - Op2: Operation#60;S2, L2#62; - state Op2 { - direction LR - [*] --> S2: HTTP Request - S2 --> [*]: HTTP Response - state L2 { - Upgrade2 : Upgrade - state Upgrade2 { - S2 - } - } - } - - Op1 --> Op2 : Plugin#colon;#colon;map -``` - -An example `Plugin` implementation can be found in [/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs). - -The service builder API requires plugins to be specified upfront - they must be passed as an argument to `builder_with_plugins` and cannot be modified afterwards. -This constraint is in place to ensure that all handlers are upgraded using the same set of plugins. - -You might find yourself wanting to apply _multiple_ plugins to your service. -This can be accommodated via [`PluginPipeline`]. - -```rust -use aws_smithy_http_server::plugin::PluginPipeline; -# use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; -# use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; - -let pipeline = PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin); -``` - -The plugins' runtime logic is executed in registration order. -In the example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. - -If you are vending a plugin, you can leverage `PluginPipeline` as an extension point: you can add custom methods to it using an extension trait. -For example: - -```rust -use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; -# use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; -# use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; - -pub trait AuthPluginExt { - fn with_auth(self) -> PluginPipeline>; -} - -impl AuthPluginExt for PluginPipeline { - fn with_auth(self) -> PluginPipeline> { - self.push(AuthPlugin) - } -} - -let pipeline = PluginPipeline::new() - .push(LoggingPlugin) - // Our custom method! - .with_auth(); -``` - ## Accessing Unmodelled Data -An additional omitted detail is that we provide an "escape hatch" allowing `Handler`s and `OperationService`s to accept data that isn't modelled. In addition to accepting `Op::Input` they can accept additional arguments which implement the [`FromParts`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/request.rs#L114-L121) trait: +An additional omitted detail is that we provide an "escape hatch" allowing `Handler`s and `OperationService`s to accept data that isn't modelled. In addition to accepting `Op::Input` they can accept additional arguments which implement the [`FromParts`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/request/trait.FromParts.html) trait: ```rust -use http::request::Parts; - +# extern crate aws_smithy_http_server; +# extern crate http; +# use http::request::Parts; +# use aws_smithy_http_server::response::IntoResponse; /// Provides a protocol aware extraction from a [`Request`]. This borrows the /// [`Parts`], in contrast to [`FromRequest`]. pub trait FromParts: Sized { @@ -707,11 +682,16 @@ pub trait FromParts: Sized { /// Extracts `self` from a [`Parts`] synchronously. fn from_parts(parts: &mut Parts) -> Result; } +# use aws_smithy_http_server::request::FromParts as FP; +# impl> FromParts

for T { +# type Rejection = >::Rejection; +# fn from_parts(parts: &mut Parts) -> Result { >::from_parts(parts) } +# } ``` -This differs from `FromRequest` trait, introduced in [Serialization and Deserialization](#serialization-and-deserialization), as it's synchronous and has non-consuming access to [`Parts`](https://docs.rs/http/0.2.8/http/request/struct.Parts.html), rather than the entire [Request](https://docs.rs/http/0.2.8/http/request/struct.Request.html). +This differs from `FromRequest` trait, introduced in [Serialization and Deserialization](#serialization-and-deserialization), as it's synchronous and has non-consuming access to [`Parts`](https://docs.rs/http/latest/http/request/struct.Parts.html), rather than the entire [Request](https://docs.rs/http/latest/http/request/struct.Request.html). -```rust +```rust,ignore pub struct Parts { pub method: Method, pub uri: Uri, @@ -725,6 +705,13 @@ pub struct Parts { This is commonly used to access types stored within [`Extensions`](https://docs.rs/http/0.2.8/http/struct.Extensions.html) which have been inserted by a middleware. An `Extension` struct implements `FromParts` to support this use case: ```rust +# extern crate aws_smithy_http_server; +# extern crate http; +# extern crate thiserror; +# use aws_smithy_http_server::{body::BoxBody, request::FromParts, response::IntoResponse}; +# use http::status::StatusCode; +# use thiserror::Error; +# fn empty() -> BoxBody { todo!() } /// Generic extension type stored in and extracted from [request extensions]. /// /// This is commonly used to share state across handlers. diff --git a/design/src/server/code_generation.md b/design/src/server/code_generation.md index b04008cb46..f65c08d2f1 100644 --- a/design/src/server/code_generation.md +++ b/design/src/server/code_generation.md @@ -1,6 +1,5 @@ # Generating Common Service Code -How a service is constructed and how to plug in new business logic is described in [Pokémon Service][1]. This document introduces the project and how code is being generated. It is written for developers who want to start contributing to `smithy-rs`. ## Folder structure @@ -12,37 +11,37 @@ The project is divided in: - `/codegen-server`: server code generation. Depends on `codegen-core` - `/aws`: the AWS Rust SDK, it deals with AWS services specifically. The folder structure reflects the project's, with the `rust-runtime` and the `codegen` - `/rust-runtime`: the generated client and server crates may depend on crates in this folder. Crates here are not code generated. The only crate that is not published is `inlineable`, -which contains common functions used by other crates, [copied into][2] the source crate +which contains common functions used by other crates, [copied into][1] the source crate Crates in `/rust-runtime` (informally referred to as "runtime crates") are added to a crate's dependency only when used. -For example, if a model uses event streams, the generated crates will depend on [`aws-smithy-eventstream`][3]. +For example, if a model uses event streams, the generated crates will depend on [`aws-smithy-eventstream`][2]. ## Generating code -`smithy-rs`'s entry points are Smithy code-generation plugins, and is not a command. One entry point is in [RustCodegenPlugin::execute][4] and -inherits from `SmithyBuildPlugin` in [smithy-build][5]. Code generation is in Kotlin and shared common, non-Rust specific code with the [`smithy` Java repository][6]. They plug into the [Smithy gradle][7] plugin, which is a gradle plugin. +`smithy-rs`'s entry points are Smithy code-generation plugins, and is not a command. One entry point is in [RustCodegenPlugin::execute][3] and +inherits from `SmithyBuildPlugin` in [smithy-build][4]. Code generation is in Kotlin and shared common, non-Rust specific code with the [`smithy` Java repository][5]. They plug into the [Smithy gradle][6] plugin, which is a gradle plugin. The comment at the beginning of `execute` describes what a `Decorator` is and uses the following terms: - Context: contains the model being generated, projection and settings for the build -- Decorator: (also referred to as customizations) customizes how code is being generated. AWS services are required to sign with the SigV4 protocol, and [a decorator][8] adds Rust code to sign requests and responses. +- Decorator: (also referred to as customizations) customizes how code is being generated. AWS services are required to sign with the SigV4 protocol, and [a decorator][7] adds Rust code to sign requests and responses. Decorators are applied in reverse order of being added and have a priority order. - Writer: creates files and adds content; it supports templating, using `#` for substitutions - Location: the file where a symbol will be written to -The only task of a `RustCodegenPlugin` is to construct a `CodegenVisitor` and call its [execute()][9] method. +The only task of a `RustCodegenPlugin` is to construct a `CodegenVisitor` and call its [execute()][8] method. -`CodegenVisitor::execute()` is given a `Context` and decorators, and calls a [CodegenVisitor][10]. +`CodegenVisitor::execute()` is given a `Context` and decorators, and calls a [CodegenVisitor][9]. CodegenVisitor, RustCodegenPlugin, and wherever there are different implementations between client and server, such as in generating error types, have corresponding server versions. Objects used throughout code generation are: -- Symbol: a node in a graph, an abstraction that represents the qualified name of a type; symbols reference and depend on other symbols, and have some common properties among languages (such as a namespace or a definition file). For Rust, we add properties to include more metadata about a symbol, such as its [type][11] -- [RustType][12]: `Option`, `HashMap`, ... along with their namespaces of origin such as `std::collections` -- [RuntimeType][13]: the information to locate a type, plus the crates it depends on -- [ShapeId][14]: an immutable object that identifies a `Shape` +- Symbol: a node in a graph, an abstraction that represents the qualified name of a type; symbols reference and depend on other symbols, and have some common properties among languages (such as a namespace or a definition file). For Rust, we add properties to include more metadata about a symbol, such as its [type][10] +- [RustType][11]: `Option`, `HashMap`, ... along with their namespaces of origin such as `std::collections` +- [RuntimeType][12]: the information to locate a type, plus the crates it depends on +- [ShapeId][13]: an immutable object that identifies a `Shape` Useful conversions are: @@ -51,9 +50,9 @@ SymbolProvider.toSymbol(shape) ``` where `SymbolProvider` constructs symbols for shapes. Some symbols require to create other symbols and types; -[event streams][15] and [other streaming shapes][16] are an example. -Symbol providers are all [applied][17] in order; if a shape uses a reserved keyword in Rust, its name is converted to a new name by a [symbol provider][18], -and all other providers will work with this [new][19] symbol. +[event streams][14] and [other streaming shapes][15] are an example. +Symbol providers are all [applied][16] in order; if a shape uses a reserved keyword in Rust, its name is converted to a new name by a [symbol provider][17], +and all other providers will work with this [new][18] symbol. ```kotlin Model.expectShape(shapeId) @@ -61,42 +60,41 @@ Model.expectShape(shapeId) Each model has a `shapeId` to `shape` map; this method returns the shape associated with this shapeId. -Some objects implement a `transform` [method][20] that only change the input model, so that code generation will work on that new model. This is used to, for example, add a trait to a shape. +Some objects implement a `transform` [method][19] that only change the input model, so that code generation will work on that new model. This is used to, for example, add a trait to a shape. -`CodegenVisitor` is a `ShapeVisitor`. For all services in the input model, shapes are [converted into Rust][21]; -[here][22] is how a service is constructed, -[here][23] a structure and so on. +`CodegenVisitor` is a `ShapeVisitor`. For all services in the input model, shapes are [converted into Rust][20]; +[here][21] is how a service is constructed, +[here][22] a structure and so on. -Code generation flows from writer to files and entities are (mostly) generated only on a [need-by-need basis][24]. -The complete result is a [Rust crate][25], -in which all dependencies are written into their modules and `lib.rs` is generated ([here][26]). -`execute()` ends by running [cargo fmt][27], +Code generation flows from writer to files and entities are (mostly) generated only on a [need-by-need basis][23]. +The complete result is a [Rust crate][24], +in which all dependencies are written into their modules and `lib.rs` is generated ([here][25]). +`execute()` ends by running [cargo fmt][26], to avoid having to format correctly Rust in `Writer`s and to be sure the generated code follows the styling rules. -[1]: ./pokemon_service.md -[2]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt#L95-L95 -[3]: https://docs.rs/aws-smithy-eventstream -[4]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt#L34 -[5]: https://github.com/awslabs/smithy/tree/main/smithy-build -[6]: https://github.com/awslabs/smithy -[7]: https://awslabs.github.io/smithy/1.0/guides/building-models/gradle-plugin.html -[8]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt#L45 -[9]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L115-L115 -[10]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L44 -[11]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt#L363-L363 -[12]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt#L25-L25 -[13]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt#L113-L113 -[14]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#shape-id -[15]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProvider.kt#L65-L65 -[16]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/StreamingTraitSymbolProvider.kt#L26-L26 -[17]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt#L62-L62 -[18]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustReservedWords.kt#L26-L26 -[19]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProvider.kt#L38-L38 -[20]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/OperationNormalizer.kt#L52-L52 -[21]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L119-L119 -[22]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L150-L150 -[23]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L172-L172 -[24]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L119-L126 -[25]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L42-L42 -[26]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L96-L107 -[27]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L133-L133 +[1]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt#L95-L95 +[2]: https://docs.rs/aws-smithy-eventstream +[3]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt#L34 +[4]: https://github.com/awslabs/smithy/tree/main/smithy-build +[5]: https://github.com/awslabs/smithy +[6]: https://awslabs.github.io/smithy/1.0/guides/building-models/gradle-plugin.html +[7]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt#L45 +[8]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L115-L115 +[9]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L44 +[10]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt#L363-L363 +[11]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt#L25-L25 +[12]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt#L113-L113 +[13]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#shape-id +[14]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProvider.kt#L65-L65 +[15]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/StreamingTraitSymbolProvider.kt#L26-L26 +[16]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt#L62-L62 +[17]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustReservedWords.kt#L26-L26 +[18]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProvider.kt#L38-L38 +[19]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/OperationNormalizer.kt#L52-L52 +[20]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L119-L119 +[21]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L150-L150 +[22]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L172-L172 +[23]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L119-L126 +[24]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L42-L42 +[25]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L96-L107 +[26]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L133-L133 diff --git a/design/src/server/from_parts.md b/design/src/server/from_parts.md index a03386cc90..98b5365ac4 100644 --- a/design/src/server/from_parts.md +++ b/design/src/server/from_parts.md @@ -6,7 +6,7 @@ But what if we, the customer, want to access data in the handler which is _not_ -```rust +```rust,ignore /// Provides a protocol aware extraction from a [`Request`]. This borrows the /// [`Parts`], in contrast to [`FromRequest`]. pub trait FromParts: Sized { @@ -22,7 +22,7 @@ Here [`Parts`](https://docs.rs/http/latest/http/request/struct.Parts.html) is th A prolific example of a `FromParts` implementation is `Extension`: -```rust +```rust,ignore /// Generic extension type stored in and extracted from [request extensions]. /// /// This is commonly used to share state across handlers. @@ -61,7 +61,7 @@ where This allows the service builder to accept the following handler -```rust +```rust,ignore async fn handler(input: ModelInput, extension: Extension) -> ModelOutput { /* ... */ } @@ -71,7 +71,7 @@ where `ModelInput` and `ModelOutput` are specified by the Smithy Operation and ` Up to 32 structures implementing `FromParts` can be provided to the handler with the constraint that they _must_ be provided _after_ the `ModelInput`: -```rust +```rust,ignore async fn handler(input: ModelInput, ext1: Extension, ext2: Extension, other: Other /* : FromParts */, /* ... */) -> ModelOutput { /* ... */ } @@ -81,7 +81,7 @@ Note that the `parts.extensions.remove::()` in `Extensions::from_parts` will The `FromParts` trait is public so customers have the ability specify their own implementations: -```rust +```rust,ignore struct CustomerDefined { /* ... */ } diff --git a/design/src/server/instrumentation.md b/design/src/server/instrumentation.md index d946e51b0e..08c72a2098 100644 --- a/design/src/server/instrumentation.md +++ b/design/src/server/instrumentation.md @@ -20,6 +20,10 @@ RUST_LOG=aws_smithy_http_server=warn,aws_smithy_http_server_python=error and ```rust +# extern crate tracing_subscriber; +# extern crate tracing; +# use tracing_subscriber::filter; +# use tracing::Level; let filter = filter::Targets::new().with_target("aws_smithy_http_server", Level::DEBUG); ``` @@ -55,14 +59,24 @@ Smithy provides an out-the-box middleware which: This is enabled via the `instrument` method provided by the `aws_smithy_http_server::instrumentation::InstrumentExt` trait. -```rust -use aws_smithy_http_server::instrumentation::InstrumentExt; - -let plugins = PluginPipeline::new().instrument(); -let app = PokemonService::builder_with_plugins(plugins) - .get_pokemon_species(/* handler */) +```rust,no_run +# extern crate aws_smithy_http_server; +# extern crate pokemon_service_server_sdk; +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +use aws_smithy_http_server::{ + instrumentation::InstrumentExt, + plugin::{IdentityPlugin, HttpPlugins} +}; +use pokemon_service_server_sdk::PokemonService; + +let http_plugins = HttpPlugins::new().instrument(); +let app = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; ``` @@ -72,6 +86,9 @@ let app = PokemonService::builder_with_plugins(plugins) The Pokémon service example, located at `/examples/pokemon-service`, sets up a `tracing` `Subscriber` as follows: ```rust +# extern crate tracing_subscriber; +use tracing_subscriber::{prelude::*, EnvFilter}; + /// Setup `tracing::subscriber` to read the log level from RUST_LOG environment variable. pub fn setup_tracing() { let format = tracing_subscriber::fmt::layer().pretty(); diff --git a/design/src/server/middleware.md b/design/src/server/middleware.md index 45334adf9e..af394b1bca 100644 --- a/design/src/server/middleware.md +++ b/design/src/server/middleware.md @@ -59,6 +59,10 @@ pub struct NewService { and a complementary ```rust +# extern crate tower; +# pub struct NewService { inner: S } +use tower::{Layer, Service}; + pub struct NewLayer { /* auxiliary data */ } @@ -129,16 +133,30 @@ stateDiagram-v2 where `UpgradeLayer` is the `Layer` converting Smithy model structures to HTTP structures and the `RoutingService` is responsible for routing requests to the appropriate operation. -### A) Outer Middleware +### A. Outer Middleware The output of the Smithy service builder provides the user with a `Service` implementation. A `Layer` can be applied around the entire `Service`. -```rust +```rust,no_run +# extern crate aws_smithy_http_server; +# extern crate pokemon_service_server_sdk; +# extern crate tower; +# use std::time::Duration; +# struct TimeoutLayer; +# impl TimeoutLayer { fn new(t: Duration) -> Self { Self }} +# impl Layer for TimeoutLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } +# use pokemon_service_server_sdk::{input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +use pokemon_service_server_sdk::PokemonService; +use tower::Layer; + // This is a HTTP `Service`. let app /* : PokemonService> */ = PokemonService::builder_without_plugins() - .get_pokemon_species(/* handler */) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; // Construct `TimeoutLayer`. let timeout_layer = TimeoutLayer::new(Duration::from_secs(3)); @@ -147,63 +165,114 @@ let timeout_layer = TimeoutLayer::new(Duration::from_secs(3)); let app = timeout_layer.layer(app); ``` -### B) Route Middleware +### B. Route Middleware A _single_ layer can be applied to _all_ routes inside the `Router`. This exists as a method on the output of the service builder. -```rust -// Construct `TraceLayer`. -let trace_layer = TraceLayer::new_for_http(Duration::from_secs(3)); +```rust,no_run +# extern crate tower; +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use tower::{util::service_fn, Layer}; +# use std::time::Duration; +# use pokemon_service_server_sdk::{input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +# struct MetricsLayer; +# impl MetricsLayer { pub fn new() -> Self { Self } } +# impl Layer for MetricsLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } +use pokemon_service_server_sdk::PokemonService; + +// Construct `MetricsLayer`. +let metrics_layer = MetricsLayer::new(); let app /* : PokemonService> */ = PokemonService::builder_without_plugins() - .get_pokemon_species(/* handler */) + .get_pokemon_species(handler) /* ... */ .build() + .unwrap() + # ; let app: PokemonService = app; + # app // Apply HTTP logging after routing. - .layer(&trace_layer); + .layer(&metrics_layer); ``` Note that requests pass through this middleware immediately _after_ routing succeeds and therefore will _not_ be encountered if routing fails. This means that the [TraceLayer](https://docs.rs/tower-http/latest/tower_http/trace/struct.TraceLayer.html) in the example above does _not_ provide logs unless routing has completed. This contrasts to [middleware A](#a-outer-middleware), which _all_ requests/responses pass through when entering/leaving the service. -### C) Operation Specific HTTP Middleware +### C. Operation Specific HTTP Middleware A "HTTP layer" can be applied to specific operations. -```rust -// Construct `TraceLayer`. -let trace_layer = TraceLayer::new_for_http(Duration::from_secs(3)); +```rust,no_run +# extern crate tower; +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use tower::{util::service_fn, Layer}; +# use std::time::Duration; +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; +# use aws_smithy_http_server::{operation::OperationShapeExt, plugin::*, operation::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +# struct LoggingLayer; +# impl LoggingLayer { pub fn new() -> Self { Self } } +# impl Layer for LoggingLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } +use pokemon_service_server_sdk::{PokemonService, scope}; + +scope! { + /// Only log on `GetPokemonSpecies` and `GetStorage` + struct LoggingScope { + includes: [GetPokemonSpecies, GetStorage] + } +} -// Apply HTTP logging to only the `GetPokemonSpecies` operation. -let layered_handler = GetPokemonSpecies::from_handler(/* handler */).layer(trace_layer); +// Construct `LoggingLayer`. +let logging_plugin = LayerPlugin(LoggingLayer::new()); +let logging_plugin = Scoped::new::(logging_plugin); +let http_plugins = HttpPlugins::new().push(logging_plugin); -let app /* : PokemonService> */ = PokemonService::builder_without_plugins() - .get_pokemon_species_operation(layered_handler) +let app /* : PokemonService> */ = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; ``` This middleware transforms the operations HTTP requests and responses. -### D) Operation Specific Model Middleware +### D. Operation Specific Model Middleware A "model layer" can be applied to specific operations. -```rust -// A handler `Service`. -let handler_svc = service_fn(/* handler */); +```rust,no_run +# extern crate tower; +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use tower::{util::service_fn, Layer}; +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +# use aws_smithy_http_server::{operation::*, plugin::*}; +# struct BufferLayer; +# impl BufferLayer { pub fn new(size: usize) -> Self { Self } } +# impl Layer for BufferLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } +use pokemon_service_server_sdk::{PokemonService, scope}; + +scope! { + /// Only buffer on `GetPokemonSpecies` and `GetStorage` + struct BufferScope { + includes: [GetPokemonSpecies, GetStorage] + } +} // Construct `BufferLayer`. -let buffer_layer = BufferLayer::new(3); - -// Apply a 3 item buffer to `handler_svc`. -let handler_svc = buffer_layer.layer(handler_svc); - -let layered_handler = GetPokemonSpecies::from_service(handler_svc); +let buffer_plugin = LayerPlugin(BufferLayer::new(3)); +let buffer_plugin = Scoped::new::(buffer_plugin); +let model_plugins = ModelPlugins::new().push(buffer_plugin); -let app /* : PokemonService> */ = PokemonService::builder_without_plugins() - .get_pokemon_species_operation(layered_handler) +let app /* : PokemonService> */ = PokemonService::builder_with_plugins(IdentityPlugin, model_plugins) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; ``` In contrast to [position C](#c-operation-specific-http-middleware), this middleware transforms the operations modelled inputs to modelled outputs. @@ -215,11 +284,17 @@ Suppose we want to apply a different `Layer` to every operation. In this case, p Consider the following middleware: ```rust +# extern crate aws_smithy_http_server; +# extern crate tower; +use aws_smithy_http_server::shape_id::ShapeId; +use std::task::{Context, Poll}; +use tower::Service; + /// A [`Service`] that adds a print log. -#[derive(Clone, Debug)] pub struct PrintService { inner: S, - name: &'static str, + operation_id: ShapeId, + service_id: ShapeId } impl Service for PrintService @@ -235,91 +310,61 @@ where } fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.name); + println!("Hi {} in {}", self.operation_id.name(), self.service_id.name()); self.inner.call(req) } } - -/// A [`Layer`] which constructs the [`PrintService`]. -#[derive(Debug)] -pub struct PrintLayer { - name: &'static str, -} -impl Layer for PrintLayer { - type Service = PrintService; - - fn layer(&self, service: S) -> Self::Service { - PrintService { - inner: service, - name: self.name, - } - } -} ``` The plugin system provides a way to construct then apply `Layer`s in position [C](#c-operation-specific-http-middleware) and [D](#d-operation-specific-model-middleware), using the [protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) and [operation shape](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service-operations) as parameters. -An example of a `PrintPlugin` which applies a layer printing the operation name: +An example of a `PrintPlugin` which prints the operation name: ```rust -/// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::shape_id::ShapeId; +# pub struct PrintService { inner: S, operation_id: ShapeId, service_id: ShapeId } +use aws_smithy_http_server::{plugin::Plugin, operation::OperationShape, service::ServiceShape}; + +/// A [`Plugin`] for a service builder to add a [`PrintService`] over operations. #[derive(Debug)] pub struct PrintPlugin; -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin where + Ser: ServiceShape, Op: OperationShape, { - type Service = S; - type Layer = Stack; + type Output = PrintService; - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: Op::NAME }) + fn apply(&self, inner: T) -> Self::Output { + PrintService { + inner, + operation_id: Op::ID, + service_id: Ser::ID, + } } } ``` -An alternative example which applies a layer for a given protocol: +You can provide a custom method to add your plugin to a collection of `HttpPlugins` or `ModelPlugins` via an extension trait. For example, for `HttpPlugins`: ```rust -/// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. -#[derive(Debug)] -pub struct PrintPlugin; - -impl Plugin for PrintPlugin -{ - type Service = S; - type Layer = Stack; +# extern crate aws_smithy_http_server; +# pub struct PrintPlugin; +# impl aws_smithy_http_server::plugin::HttpMarker for PrintPlugin { } +use aws_smithy_http_server::plugin::{HttpPlugins, PluginStack}; - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: "AWS REST JSON v1" }) - } -} - -impl Plugin for PrintPlugin -{ - type Service = S; - type Layer = Stack; - - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: "AWS REST XML" }) - } -} -``` - -You can provide a custom method to add your plugin to a `PluginPipeline` via an extension trait: - -```rust -/// This provides a [`print`](PrintExt::print) method on [`PluginPipeline`]. +/// This provides a [`print`](PrintExt::print) method on [`HttpPlugins`]. pub trait PrintExt { /// Causes all operations to print the operation name when called. /// /// This works by applying the [`PrintPlugin`]. - fn print(self) -> PluginPipeline>; + fn print(self) -> HttpPlugins>; } -impl PrintExt for PluginPipeline { - fn print(self) -> PluginPipeline> { +impl PrintExt for HttpPlugins { + fn print(self) -> HttpPlugins> { self.push(PrintPlugin) } } @@ -327,16 +372,31 @@ impl PrintExt for PluginPipeline Plugin for PrintPlugin { type Output = T; fn apply(&self, svc: T) -> Self::Output { svc }} +# impl aws_smithy_http_server::plugin::HttpMarker for PrintPlugin { } +# trait PrintExt { fn print(self) -> HttpPlugins>; } +# impl PrintExt for HttpPlugins { fn print(self) -> HttpPlugins> { self.push(PrintPlugin) }} +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +use aws_smithy_http_server::plugin::{IdentityPlugin, HttpPlugins}; +use pokemon_service_server_sdk::PokemonService; + +let http_plugins = HttpPlugins::new() // [..other plugins..] // The custom method! .print(); -let app /* : PokemonService> */ = PokemonService::builder_with_plugins(plugin_pipeline) - .get_pokemon_species_operation(layered_handler) +let app /* : PokemonService> */ = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; ``` The custom `print` method hides the details of the `Plugin` trait from the average consumer. -They interact with the utility methods on `PluginPipeline` and enjoy the self-contained documentation. +They interact with the utility methods on `HttpPlugins` and enjoy the self-contained documentation. diff --git a/design/src/server/overview.md b/design/src/server/overview.md index 65cff8d606..7592d7278c 100644 --- a/design/src/server/overview.md +++ b/design/src/server/overview.md @@ -7,4 +7,3 @@ Smithy Rust provides the ability to generate a server whose operations are provi - [Accessing Un-modelled Data](./from_parts.md) - [The Anatomy of a Service](./anatomy.md) - [Generating Common Service Code](./code_generation.md) -- [Generating the Pokémon Service](./pokemon_service.md) diff --git a/design/src/server/pokemon_service.md b/design/src/server/pokemon_service.md deleted file mode 100644 index cb90f48dd0..0000000000 --- a/design/src/server/pokemon_service.md +++ /dev/null @@ -1,236 +0,0 @@ -# Generating the Pokémon Service - -This is an overview of client and server of the Pokémon service. It introduces: - -- How a smithy-rs server customer uses the vanilla SDK and writes their business logic -- What the runtime is and how code is generated -- The folder structure of the project - -All the code shown and linked to is from the repository at this commit: [db48039065bec890ef387385773b37154b555b14][1] - -The Smithy model used to generate the code snippets is: [Pokémon][2] - -## Building the service - -The entry point of a service is [main.rs][3] - -The `PokemonService` service in the `pokemon.smithy` has these operations and resources: - -```smithy -resources: [PokemonSpecies, Storage], -operations: [GetServerStatistics, EmptyOperation, CapturePokemonOperation, HealthCheckOperation], -``` - -The entry app is constructed as: - -```rust -let app: Router = OperationRegistryBuilder::default() -``` - -`OperationRegistryBuilder` is a struct, generated [here][4], -used by service implementors to register, for each operation, the operation's implementation logic, input and output. - -```rust -pub struct OperationRegistry { - capture_pokemon_operation: Op0, - empty_operation: Op1, - get_pokemon_species: Op2, - get_server_statistics: Op3, - get_storage: Op4, - health_check_operation: Op5, - _phantom: std::marker::PhantomData<(B, In0, In1, In2, In3, In4, In5)>, -} -``` - -The builder is constructed by a `OperationRegistryBuilder`; if an operation is not passed to the builder, it will return an error. - -```rust -let app: Router = OperationRegistryBuilder::default() - .get_pokemon_species(get_pokemon_species) - .get_storage(get_storage) - .get_server_statistics(get_server_statistics) - .capture_pokemon_operation(capture_pokemon) - .empty_operation(empty_operation) - .health_check_operation(health_check_operation) - .build() - .expect("Unable to build operation registry") - .into(); -``` - -Each of these operations is a function that can take any of these signatures. - -1. If the operation is not fallible and does not share any state: - - ```rust - pub async fn health_check_operation(_input: input::HealthCheckOperationInput) -> output::HealthCheckOperationOutput {...} - ``` - -2. If the operation is fallible and does not share any state: - - ```rust - pub async fn capture_pokemon( - mut input: input::CapturePokemonOperationInput, - ) -> Result {...} - ``` - -3. If the operation is not fallible and shares some state: - - ```rust - pub async fn get_server_statistics( - _input: input::GetServerStatisticsInput, - state: Extension>, - ) -> output::GetServerStatisticsOutput {...} - ``` - -4. If the operation is fallible and shares some state: - - ```rust - pub async fn get_storage( - input: input::GetStorageInput, - _state: Extension>, - ) -> Result {...} - ``` - -All of these are operations which implementors define; they are the business logic of the application. The rest is code generated. - -The `OperationRegistry` builds into a `Router` (`let app: Router = OperationRegistryBuilder...build().into()`). -The implementation is code generated [here][5]. - -```rust -impl - std::convert::From< - OperationRegistry, - > for aws_smithy_http_server::routing::Router -where - B: Send + 'static, - Op0: crate::server_operation_handler_trait::Handler< - B, - In0, - crate::input::CapturePokemonOperationInput, - >, - In0: 'static + Send, - ... for all Op, In -{ - fn from( - registry: OperationRegistry, - ) -> Self {...} -} -``` - -For each operation, it registers a route; the specifics depend on the [protocol][6]. -The PokemonService uses [restJson1][7] as its protocol, an operation like the `HealthCheckOperation` will be rendered as: - -```rust -let capture_pokemon_operation_request_spec = aws_smithy_http_server::routing::request_spec::RequestSpec::new( - http::Method::POST, - aws_smithy_http_server::routing::request_spec::UriSpec::new( - aws_smithy_http_server::routing::request_spec::PathAndQuerySpec::new( - aws_smithy_http_server::routing::request_spec::PathSpec::from_vector_unchecked(vec![ - aws_smithy_http_server::routing::request_spec::PathSegment::Literal(String::from("capture-pokemon-event")), - aws_smithy_http_server::routing::request_spec::PathSegment::Label, - ]), - aws_smithy_http_server::routing::request_spec::QuerySpec::from_vector_unchecked(vec![]))),); -``` - -because the URI is `/capture-pokemon-event/{region}`, with method `POST` and `region` a `Label` (then passed to the operation with its `CapturePokemonOperationInput` input struct). - -Finally, it creates a RestJSON `Router`, because that is the service's protocol. -You will have noticed, each operation is implemented as a `pub async fn`. Each operation is wrapped into an `OperationHandler`, generated [here][8]. -`OperationHandler` implements tower's `Service` [trait][9]. Implementing `Service` means that -the business logic is written as protocol-agnostic and clients request a service by calling into them, similar to an RPC call. - -```rust -aws_smithy_http_server::routing::Router::new_rest_json_router(vec![ - { - let svc = crate::server_operation_handler_trait::operation( - registry.capture_pokemon_operation, - ); -``` - -At this level, logging might be prohibited by the [`@sensitive`][10] trait. If there are no `@sensitive` shapes, the generated code looks like: - -```rust -let request_fmt = aws_smithy_http_server::instrumentation::sensitivity::RequestFmt::new(); -let response_fmt = aws_smithy_http_server::instrumentation::sensitivity::ResponseFmt::new(); -let svc = aws_smithy_http_server::instrumentation::InstrumentOperation::new( - svc, - "capture_pokemon_operation", -) -.request_fmt(request_fmt) -.response_fmt(response_fmt); -``` - -Accessing the Pokédex is modeled as a restricted operation: a passcode is needed by the Pokémon trainer. -To not log the passcode, the code will be generated [here][11] as: - -```rust -let request_fmt = aws_smithy_http_server::instrumentation::sensitivity::RequestFmt::new() - .header(|name: &http::header::HeaderName| { - #[allow(unused_variables)] - let name = name.as_str(); - let name_match = matches!(name, "passcode"); - let key_suffix = None; - let value = name_match; - aws_smithy_http_server::instrumentation::sensitivity::headers::HeaderMarker { - value, - key_suffix, - } - }) - .label(|index: usize| matches!(index, 1)); -let response_fmt = aws_smithy_http_server::instrumentation::sensitivity::ResponseFmt::new(); -``` - -Each route is a pair, [`BoxCloneService`][12] wrapping the service operation (the implementation) and -the information to consume the service operation. - -```rust -(tower::util::BoxCloneService::new(svc), capture_pokemon_operation_request_spec) -``` - -Now the `Router` is built. `Router` is not code generated, it instead lives in the [`aws-smithy-http-server`][13] crate. -We write Rust code in the runtime to: - -- Aid development of the project -- Have service-specific code that the majority of services share in the runtime - In Kotlin we generate code that is service-specific. - -A `Router` is a [`tower::Service`][9] that routes requests to the implemented services; hence it [implements][14] `Service` -like the other operations. - -The `Router` [implements][15] -the `tower::Layer` trait. Middleware are added as layers. The Pokémon example uses them: - -```rust -let shared_state = Arc::new(State::default()); -let app = app.layer( - ServiceBuilder::new() - .layer(TraceLayer::new_for_http()) - .layer(AddExtensionLayer::new(shared_state)), -); -``` - -The service is run by a [Hyper server][16]: - -```rust -hyper::Server::bind(&bind).serve(app.into_make_service()); -``` - -Generation of objects common to services, such as shapes, is described in [Code Generation][17]. - -[1]: https://github.com/awslabs/smithy-rs/tree/db48039065bec890ef387385773b37154b555b14 -[2]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server-test/model/pokemon.smithy -[3]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/main.rs#L34 -[4]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt#L1 -[5]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt#L285 -[6]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Protocol.kt#L81 -[7]: https://awslabs.github.io/smithy/1.0/spec/aws/aws-restjson1-protocol.html -[8]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt#L30 -[9]: https://docs.rs/tower-service/latest/tower_service/trait.Service.html -[10]: https://awslabs.github.io/smithy/1.0/spec/core/documentation-traits.html#sensitive-trait -[11]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt#L58 -[12]: https://docs.rs/tower/latest/tower/util/struct.BoxCloneService.html -[13]: https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/ -[14]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/rust-runtime/aws-smithy-http-server/src/routing/mod.rs#L302 -[15]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/rust-runtime/aws-smithy-http-server/src/routing/mod.rs#L146 -[16]: https://docs.rs/hyper/latest/hyper/server/struct.Server.html -[17]: ./code_generation.md diff --git a/design/src/smithy/aggregate_shapes.md b/design/src/smithy/aggregate_shapes.md index 627ea875b3..0dafac77f9 100644 --- a/design/src/smithy/aggregate_shapes.md +++ b/design/src/smithy/aggregate_shapes.md @@ -28,7 +28,7 @@ Smithy `structure` becomes a `struct` in Rust. Backwards compatibility & usabili 2. All structs are marked `#[non_exhaustive]` 3. All structs derive `Debug` & `PartialEq`. Structs **do not** derive `Eq` because a `float` member may be added in the future. 4. Struct fields are public. Public struct fields allow for [split borrows](https://doc.rust-lang.org/nomicon/borrow-splitting.html). When working with output objects this significantly improves ergonomics, especially with optional fields. - ```rust + ```rust,ignore let out = dynamo::ListTablesOutput::new(); out.some_field.unwrap(); // <- partial move, impossible with an accessor ``` @@ -52,7 +52,7 @@ long ReadIOs long WriteIOs ``` **Rust Output**: -```rust +```rust,ignore ///

Contains I/O usage metrics for a command that was invoked.

#[non_exhaustive] #[derive(std::clone::Clone, std::cmp::PartialEq)] @@ -151,7 +151,7 @@ list BoolList { } ``` **Rust**: -```rust +```rust,ignore #[non_exhaustive] #[derive(std::clone::Clone, std::cmp::PartialEq, std::fmt::Debug)] pub enum AttributeValue { diff --git a/design/src/smithy/backwards-compat.md b/design/src/smithy/backwards-compat.md index d39c071d6b..85a3ea5fd6 100644 --- a/design/src/smithy/backwards-compat.md +++ b/design/src/smithy/backwards-compat.md @@ -62,7 +62,7 @@ Specifically, [`#[non_exhaustive]`](https://doc.rust-lang.org/reference/attribut following patterns: 1. Direct structure instantiation: - ```rust + ```rust,ignore # fn foo() { let ip_addr = IpAddress { addr: "192.168.1.1" }; # } @@ -72,20 +72,20 @@ following patterns: our structures while maintaining backwards compatibility, all structures expose a builder, accessible at `SomeStruct::Builder`: - ```rust + ```rust,ignore # fn foo() { let ip_addr = IpAddress::builder().addr("192.168.1.1").build(); # } ``` 2. Structure destructuring: - ```rust + ```rust,ignore # fn foo() { let IpAddress { addr } = some_ip_addr(); # } ``` This will also fail to compile if a new member is added, however, by adding `#[non_exhaustive]`, the `..` multifield wildcard MUST be added to support new fields being added in the future: - ```rust + ```rust,ignore # fn foo() { let IpAddress { addr, .. } = some_ip_addr(); # } @@ -110,7 +110,7 @@ because new fields cannot be added to union variants, the union variants themsel to be `#[non_exhaustive]`. To support new variants from services, each union contains an `Unknown` variant. By marking `Unknown` as non_exhaustive, we prevent customers from instantiating it directly. -```rust +```rust,ignore #[non_exhaustive] #[derive(std::clone::Clone, std::cmp::PartialEq, std::fmt::Debug)] pub enum AttributeValue { diff --git a/design/src/smithy/endpoint.md b/design/src/smithy/endpoint.md index 699f170209..bef52bdfeb 100644 --- a/design/src/smithy/endpoint.md +++ b/design/src/smithy/endpoint.md @@ -4,7 +4,7 @@ The core codegen generates HTTP requests that do not contain an authority, scheme or post. These properties must be set later based on configuration. Existing AWS services have a number of requirements that increase the complexity: 1. Endpoints must support manual configuration by end users: -```rust +```rust,ignore let config = dynamodb::Config::builder() .endpoint(StaticEndpoint::for_uri("http://localhost:8000")) ``` @@ -14,7 +14,7 @@ When a user specifies a custom endpoint URI, _typically_ they will want to avoid 2. Endpoints must support being customized on a per-operation basis by the endpoint trait. This will prefix the base endpoint, potentially driven by fields of the operation. [Docs](https://awslabs.github.io/smithy/1.0/spec/core/endpoint-traits.html#endpoint-trait) 3. Endpoints must support being customized by [endpoint discovery](https://awslabs.github.io/smithy/1.0/spec/aws/aws-core.html#client-endpoint-discovery). A request, customized by a predefined set of fields from the input operation is dispatched to a specific URI. That operation returns the endpoint that should be used. Endpoints must be cached by a cache key containing: -``` +```markdown (access_key_id, [all input fields], operation) ``` Endpoints retrieved in this way specify a TTL. @@ -29,7 +29,7 @@ Configuration objects for services _must_ contain an `Endpoint`. This endpoint m During operation construction (see [Operation Construction](../transport/operation.md#operation-construction)) an `EndpointPrefix` may be set on the property bag. The eventual endpoint middleware will search for this in the property bag and (depending on the URI mutability) utilize this prefix when setting the endpoint. In the case of endpoint discovery, we envision a different pattern: -```rust +```rust,ignore // EndpointClient manages the endpoint cache let (tx, rx) = dynamodb::EndpointClient::new(); let client = aws_hyper::Client::new(); diff --git a/design/src/smithy/simple_shapes.md b/design/src/smithy/simple_shapes.md index 171196d0d7..9e2c99b434 100644 --- a/design/src/smithy/simple_shapes.md +++ b/design/src/smithy/simple_shapes.md @@ -51,7 +51,7 @@ Current models represent strings as `String`. Smithy defines the concept of "Document Types": > [Documents represent] protocol-agnostic open content that is accessed like JSON data. Open content is useful for modeling unstructured data that has no schema, data that can't be modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. The serialization format of a document is an implementation detail of a protocol and MUST NOT have any effect on the types exposed by tooling to represent a document value. -```rust +```rust,ignore {{#include ../../../rust-runtime/aws-smithy-types/src/lib.rs:document}} ``` diff --git a/design/src/transport/connector.md b/design/src/transport/connector.md new file mode 100644 index 0000000000..edb50d4412 --- /dev/null +++ b/design/src/transport/connector.md @@ -0,0 +1,7 @@ +The Smithy client provides a default TLS connector, but a custom one can be +plugged in. `rustls` is enabled with the feature flag `rustls`. + +The client had previously (prior to version 0.56.0) supported `native-tls`. You +can continue to use a client whose TLS implementation is backed by `native-tls` +by passing in a custom connector. Check out the `custom_connectors.rs` tests in +the `pokemon-service-tls` example crate. diff --git a/design/src/transport/operation.md b/design/src/transport/operation.md index 332e607657..9ca8a4dfc9 100644 --- a/design/src/transport/operation.md +++ b/design/src/transport/operation.md @@ -16,7 +16,7 @@ This section details the flow of a request through the SDK until a response is r A customer interacts with the SDK builders to construct an input. The `build()` method on an input returns an `Operation`. This codifies the base HTTP request & all the configuration and middleware layers required to modify and dispatch the request. -```rust +```rust,ignore pub struct Operation { request: Request, response_handler: H, @@ -37,7 +37,7 @@ By using a property bag, we can define the `Operation` in Smithy core. AWS speci In order to construct an operation, the generated code injects appropriate middleware & configuration via the configuration property bag. It does this by reading the configuration properties out of the service config, copying them as necessary, and loading them into the `Request`: -```rust +```rust,ignore // This is approximately the generated code, I've cleaned a few things up for readability. pub fn build(self, config: &dynamodb::config::Config) -> Operation { let op = BatchExecuteStatement::new(BatchExecuteStatementInput { diff --git a/examples/README.md b/examples/README.md index 7f08738455..f179bc1a44 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,17 +1,35 @@ # Smithy Rust Server SDK examples -This folder contains an example services showcasing the service framework capabilities and to run benchmarks. +This folder contains some example services showcasing Smithy Rust Server SDK, +also known as the Rust service framework, capabilities and to run benchmarks. -- `/pokemon-service`, a HTTP server implementation demonstrating [middleware](https://awslabs.github.io/smithy-rs/design/server/middleware.html) -and [extractors](https://awslabs.github.io/smithy-rs/design/server/from_parts.html). -- `/pokemon-service-tls`, a minimal HTTPS server implementation. -- `/pokemon-service-lambda`, a minimal Lambda deployment. +Three server implementations are available: -The `/{binary}/tests` folders are integration tests involving the generated clients. +- `/pokemon-service`, a HTTP server demonstrating [middleware] and [extractors]. +- `/pokemon-service-tls`, a HTTPS server. This server can do + its own TLS negotiation, rather than relying on a load balancer. +- `/pokemon-service-lambda`, a server that can be deployed onto AWS Lambda. -## Build +These servers, and their clients, are generated using smithy-rs. You're invited +to benchmark the performance of these servers to see whether smithy-rs might be +a suitable choice for implementing your web service. -Since this example requires both the server and client SDK to be code-generated +[middleware]: https://awslabs.github.io/smithy-rs/design/server/middleware.html +[extractors]: https://awslabs.github.io/smithy-rs/design/server/from_parts.html + + +## Pre-requisites + +You will need install Java 11 to run the smithy-rs code generator and an +installation of Rust, including `cargo`, to compile the generated code. + +(Optional) The [Cargo Lambda](https://cargo-lambda.info/) sub-command for +`cargo` is required to support the AWS Lambda integration. + + +## Building + +Since these examples require both the server and client SDK to be code-generated from their [model](/codegen-server-test/model/pokemon.smithy), a Makefile is provided to build and run the service. Just run `make` to prepare the first build. @@ -19,31 +37,52 @@ build. Once the example has been built successfully the first time, idiomatic `cargo` can be used directly. -`make distclean` can be used for a complete cleanup of all artefacts. +### Make targets: + +- `codegen`: generates the Pokémon service crates (default) +- `build`: compiles the generated client and server +- `clean`: deletes build artifacts +- `clippy`: lints the code +- `distclean`: delete generated code and build artifacts +- `doc-open`: builds and opens the rustdoc documentation +- `lambda_invoke`: invokes a running server +- `lambda_watch`: runs the service on an emulated AWS Lambda environment +- `run`: runs the Pokémon service +- `test`: runs integration and unit tests + -## Run +## Running services -To run a binary use +To run one of the three server implementations locally, provide the appropriate +service name to the `--bin` flag: ```bash -cargo run -p $BINARY +cargo run --bin pokemon-service[(-lambda|-tls)] ``` -CLI arguments can be passed to the servers, use +CLI arguments can be passed to the server binaries by adding them after `--`. +For example, to see a service's help information, use the following: ```bash -cargo run -p $BINARY -- --help +cargo run --bin -- --help ``` -for more information. +## Testing -## Test +The `/pokemon-test*/tests` folders provide integration tests involving the +generated clients. -`cargo test` can be used to spawn a service and run some simple integration -tests against it. Use `-p $BINARY` to filter by package. +They can be invoked with `cargo test`. This will spawn each service in turn +and run some integration tests against it. Use `-p ` to filter by +package. More info can be found in the `tests` folder of each package. -## Benchmarks -Please see [BENCHMARKS.md](/examples/BENCHMARKS.md). +## Benchmarking + +Servers running locally (see "Running services") can be benchmarked with any +load testing tool, such as Artillery or `wrk`. + +Please see [BENCHMARKS.md](/examples/BENCHMARKS.md) for benchmarking results +produced by the smithy-rs team. diff --git a/examples/pokemon-service-common/Cargo.toml b/examples/pokemon-service-common/Cargo.toml index 704055c813..f2c86eee0e 100644 --- a/examples/pokemon-service-common/Cargo.toml +++ b/examples/pokemon-service-common/Cargo.toml @@ -13,12 +13,15 @@ rand = "0.8" tracing = "0.1" tracing-subscriber = { version = "0.3.16", features = ["env-filter", "json"] } tokio = { version = "1", default-features = false, features = ["time"] } +tower = "0.4" # Local paths +aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client" } aws-smithy-http = { path = "../../rust-runtime/aws-smithy-http" } aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server" } pokemon-service-client = { path = "../pokemon-service-client" } pokemon-service-server-sdk = { path = "../pokemon-service-server-sdk" } [dev-dependencies] -tower = "0.4" +aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client", features = ["test-util"] } +aws-smithy-runtime = { path = "../../rust-runtime/aws-smithy-runtime", features = ["test-util"] } diff --git a/examples/pokemon-service-common/src/lib.rs b/examples/pokemon-service-common/src/lib.rs index f431270da6..cb5c4bd604 100644 --- a/examples/pokemon-service-common/src/lib.rs +++ b/examples/pokemon-service-common/src/lib.rs @@ -15,12 +15,15 @@ use std::{ }; use async_stream::stream; -use aws_smithy_http::operation::Request; +use aws_smithy_client::{conns, hyper_ext::Adapter}; +use aws_smithy_http::{body::SdkBody, byte_stream::ByteStream}; use aws_smithy_http_server::Extension; +use http::Uri; use pokemon_service_server_sdk::{ error, input, model, model::CapturingPayload, output, types::Blob, }; -use rand::Rng; +use rand::{seq::SliceRandom, Rng}; +use tower::Service; use tracing_subscriber::{prelude::*, EnvFilter}; const PIKACHU_ENGLISH_FLAVOR_TEXT: &str = @@ -32,16 +35,6 @@ const PIKACHU_ITALIAN_FLAVOR_TEXT: &str = const PIKACHU_JAPANESE_FLAVOR_TEXT: &str = "ほっぺたの りょうがわに ちいさい でんきぶくろを もつ。ピンチのときに ほうでんする。"; -/// Rewrites the base URL of a request -pub fn rewrite_base_url(base_url: String) -> impl Fn(Request) -> Request + Clone { - move |mut req| { - let http_req = req.http_mut(); - let uri = format!("{base_url}{}", http_req.uri().path()); - *http_req.uri_mut() = uri.parse().unwrap(); - req - } -} - /// Kills [`Child`] process when dropped. #[derive(Debug)] #[must_use] @@ -318,6 +311,37 @@ pub async fn check_health(_input: input::CheckHealthInput) -> output::CheckHealt output::CheckHealthOutput {} } +const RADIO_STREAMS: [&str; 2] = [ + "https://ia800107.us.archive.org/33/items/299SoundEffectCollection/102%20Palette%20Town%20Theme.mp3", + "https://ia600408.us.archive.org/29/items/PocketMonstersGreenBetaLavenderTownMusicwwwFlvtoCom/Pocket%20Monsters%20Green%20Beta-%20Lavender%20Town%20Music-%5Bwww_flvto_com%5D.mp3", +]; + +/// Streams a random Pokémon song. +pub async fn stream_pokemon_radio( + _input: input::StreamPokemonRadioInput, +) -> output::StreamPokemonRadioOutput { + let radio_stream_url = RADIO_STREAMS + .choose(&mut rand::thread_rng()) + .expect("`RADIO_STREAMS` is empty") + .parse::() + .expect("Invalid url in `RADIO_STREAMS`"); + + let mut connector = Adapter::builder().build(conns::https()); + let result = connector + .call( + http::Request::builder() + .uri(radio_stream_url) + .body(SdkBody::empty()) + .unwrap(), + ) + .await + .unwrap(); + + output::StreamPokemonRadioOutput { + data: ByteStream::new(result.into_body()), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/examples/pokemon-service-common/tests/plugins_execution_order.rs b/examples/pokemon-service-common/tests/plugins_execution_order.rs index 78dccecde1..fb5f0fb4d7 100644 --- a/examples/pokemon-service-common/tests/plugins_execution_order.rs +++ b/examples/pokemon-service-common/tests/plugins_execution_order.rs @@ -11,14 +11,11 @@ use std::{ }; use aws_smithy_http::body::SdkBody; -use aws_smithy_http_server::{ - operation::Operation, - plugin::{Plugin, PluginPipeline}, -}; -use tower::layer::util::Stack; +use aws_smithy_http_server::plugin::{HttpMarker, HttpPlugins, IdentityPlugin, Plugin}; use tower::{Layer, Service}; -use pokemon_service_client::{operation::do_nothing::DoNothingInput, Config}; +use aws_smithy_client::test_connection::capture_request; +use pokemon_service_client::{Client, Config}; use pokemon_service_common::do_nothing; trait OperationExt { @@ -38,19 +35,26 @@ async fn plugin_layers_are_executed_in_registration_order() { // We can then check the vector content to verify the invocation order let output = Arc::new(Mutex::new(Vec::new())); - let pipeline = PluginPipeline::new() + let http_plugins = HttpPlugins::new() .push(SentinelPlugin::new("first", output.clone())) .push(SentinelPlugin::new("second", output.clone())); - let mut app = pokemon_service_server_sdk::PokemonService::builder_with_plugins(pipeline) - .do_nothing(do_nothing) - .build_unchecked(); - let request = DoNothingInput::builder() - .build() - .unwrap() - .make_operation(&Config::builder().build()) - .await - .unwrap() - .into_http(); + let mut app = pokemon_service_server_sdk::PokemonService::builder_with_plugins( + http_plugins, + IdentityPlugin, + ) + .do_nothing(do_nothing) + .build_unchecked(); + + let request = { + let (conn, rcvr) = capture_request(None); + let config = Config::builder() + .http_connector(conn) + .endpoint_url("http://localhost:1234") + .build(); + Client::from_conf(config).do_nothing().send().await.unwrap(); + rcvr.expect_request() + }; + app.call(request).await.unwrap(); let output_guard = output.lock().unwrap(); @@ -64,25 +68,24 @@ struct SentinelPlugin { impl SentinelPlugin { pub fn new(name: &'static str, output: Arc>>) -> Self { - Self { - name, - output: output, - } + Self { name, output } } } -impl Plugin for SentinelPlugin { - type Service = S; - type Layer = Stack; +impl Plugin for SentinelPlugin { + type Output = SentinelService; - fn map(&self, input: Operation) -> Operation { - input.layer(SentinelLayer { + fn apply(&self, inner: T) -> Self::Output { + SentinelService { + inner, name: self.name, output: self.output.clone(), - }) + } } } +impl HttpMarker for SentinelPlugin {} + /// A [`Service`] that adds a print log. #[derive(Clone, Debug)] pub struct SentinelService { diff --git a/examples/pokemon-service-lambda/Cargo.toml b/examples/pokemon-service-lambda/Cargo.toml index 96a2d18230..137397de01 100644 --- a/examples/pokemon-service-lambda/Cargo.toml +++ b/examples/pokemon-service-lambda/Cargo.toml @@ -9,11 +9,14 @@ description = "A smithy Rust service to retrieve information about Pokémon via [dependencies] async-stream = "0.3.4" clap = { version = "4.1.11", features = ["derive"] } -hyper = {version = "0.14.25", features = ["server"] } +hyper = {version = "0.14.26", features = ["server"] } tokio = "1.26.0" tracing = "0.1" -lambda_http = "0.7.3" +# `aws-smithy-http-server` is only guaranteed to be compatible with this +# version of `lambda_http`, or semver-compatible versions of this version. +# Depending on other versions of `lambda_http` may not work. +lambda_http = "0.8.0" # Local paths aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server", features = ["aws-lambda"] } diff --git a/examples/pokemon-service-lambda/src/main.rs b/examples/pokemon-service-lambda/src/main.rs index 2e80cb9802..2f08e072b5 100644 --- a/examples/pokemon-service-lambda/src/main.rs +++ b/examples/pokemon-service-lambda/src/main.rs @@ -8,13 +8,16 @@ use std::sync::Arc; use aws_smithy_http_server::{routing::LambdaHandler, AddExtensionLayer}; use pokemon_service_common::{ - capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, State, + capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, + setup_tracing, stream_pokemon_radio, State, }; use pokemon_service_lambda::get_storage_lambda; use pokemon_service_server_sdk::PokemonService; #[tokio::main] pub async fn main() { + setup_tracing(); + let app = PokemonService::builder_without_plugins() // Build a registry containing implementations to all the operations in the service. These // are async functions or async closures that take as input the operation's input and @@ -25,6 +28,7 @@ pub async fn main() { .capture_pokemon(capture_pokemon) .do_nothing(do_nothing) .check_health(check_health) + .stream_pokemon_radio(stream_pokemon_radio) .build() .expect("failed to build an instance of PokemonService") // Set up shared state and middlewares. diff --git a/examples/pokemon-service-tls/Cargo.toml b/examples/pokemon-service-tls/Cargo.toml index ac3b32b7fe..9f11a904fc 100644 --- a/examples/pokemon-service-tls/Cargo.toml +++ b/examples/pokemon-service-tls/Cargo.toml @@ -8,12 +8,12 @@ description = "A smithy Rust service to retrieve information about Pokémon." [dependencies] clap = { version = "4.1.11", features = ["derive"] } -hyper = { version = "0.14.25", features = ["server"] } +hyper = { version = "0.14.26", features = ["server"] } tokio = "1.26.0" # These dependencies are only required for the `pokemon-service-tls` program. -tls-listener = { version = "0.6.0", features = ["rustls", "hyper-h2"] } -tokio-rustls = "0.23.4" +tls-listener = { version = "0.7.0", features = ["rustls", "hyper-h2"] } +tokio-rustls = "0.24.0" rustls-pemfile = "1.0.2" futures-util = { version = "0.3.27", default-features = false } @@ -26,8 +26,9 @@ pokemon-service-common = { path = "../pokemon-service-common/" } assert_cmd = "2.0" serial_test = "1.0.0" -# This dependency is only required for testing the `pokemon-service-tls` program. -hyper-rustls = { version = "0.23.2", features = ["http2"] } +# These dependencies are only required for testing the `pokemon-service-tls` program. +hyper-rustls = { version = "0.24", features = ["http2"] } +hyper-tls = { version = "0.5" } # Local paths aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client/", features = ["rustls"] } diff --git a/examples/pokemon-service-tls/src/main.rs b/examples/pokemon-service-tls/src/main.rs index 2a9ef679d2..a67d145042 100644 --- a/examples/pokemon-service-tls/src/main.rs +++ b/examples/pokemon-service-tls/src/main.rs @@ -34,7 +34,7 @@ use tokio_rustls::{ use pokemon_service_common::{ capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, - get_storage, setup_tracing, State, + get_storage, setup_tracing, stream_pokemon_radio, State, }; use pokemon_service_server_sdk::PokemonService; use pokemon_service_tls::{DEFAULT_ADDRESS, DEFAULT_PORT, DEFAULT_TEST_CERT, DEFAULT_TEST_KEY}; @@ -71,6 +71,7 @@ pub async fn main() { .capture_pokemon(capture_pokemon) .do_nothing(do_nothing) .check_health(check_health) + .stream_pokemon_radio(stream_pokemon_radio) .build() .expect("failed to build an instance of PokemonService") // Set up shared state and middlewares. diff --git a/examples/pokemon-service-tls/tests/common/mod.rs b/examples/pokemon-service-tls/tests/common/mod.rs index 746b7823e2..0d69407cf7 100644 --- a/examples/pokemon-service-tls/tests/common/mod.rs +++ b/examples/pokemon-service-tls/tests/common/mod.rs @@ -6,21 +6,16 @@ use std::{fs::File, io::BufReader, process::Command, time::Duration}; use assert_cmd::prelude::*; -use aws_smithy_client::{ - erase::{DynConnector, DynMiddleware}, - hyper_ext::Adapter, -}; +use aws_smithy_client::hyper_ext::Adapter; use tokio::time::sleep; -use pokemon_service_client::{Builder, Client, Config}; -use pokemon_service_common::{rewrite_base_url, ChildDrop}; +use pokemon_service_client::{Client, Config}; +use pokemon_service_common::ChildDrop; use pokemon_service_tls::{DEFAULT_DOMAIN, DEFAULT_PORT, DEFAULT_TEST_CERT}; pub async fn run_server() -> ChildDrop { - let child = Command::cargo_bin("pokemon-service-tls") - .unwrap() - .spawn() - .unwrap(); + let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); + let child = Command::cargo_bin(crate_name).unwrap().spawn().unwrap(); sleep(Duration::from_millis(500)).await; @@ -29,7 +24,7 @@ pub async fn run_server() -> ChildDrop { // Returns a client that only talks through https and http2 connections. // It is useful in testing whether our server can talk to http2. -pub fn client_http2_only() -> Client> { +pub fn client_http2_only() -> Client { // Create custom cert store and add our test certificate to prevent unknown cert issues. let mut reader = BufReader::new(File::open(DEFAULT_TEST_CERT).expect("could not open certificate")); @@ -48,11 +43,39 @@ pub fn client_http2_only() -> Client> .enable_http2() .build(); - let base_url = format!("https://{DEFAULT_DOMAIN}:{DEFAULT_PORT}"); - let raw_client = Builder::new() - .connector(DynConnector::new(Adapter::builder().build(connector))) - .middleware_fn(rewrite_base_url(base_url)) - .build_dyn(); - let config = Config::builder().build(); - Client::with_config(raw_client, config) + let config = Config::builder() + .http_connector(Adapter::builder().build(connector)) + .endpoint_url(format!("https://{DEFAULT_DOMAIN}:{DEFAULT_PORT}")) + .build(); + Client::from_conf(config) +} + +/// A `hyper` connector that uses the `native-tls` crate for TLS. To use this in a smithy client, +/// wrap it in a [aws_smithy_client::hyper_ext::Adapter]. +pub type NativeTlsConnector = hyper_tls::HttpsConnector; + +fn native_tls_connector() -> NativeTlsConnector { + let cert = hyper_tls::native_tls::Certificate::from_pem( + std::fs::read_to_string(DEFAULT_TEST_CERT) + .expect("could not open certificate") + .as_bytes(), + ) + .expect("could not parse certificate"); + + let tls_connector = hyper_tls::native_tls::TlsConnector::builder() + .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12)) + .add_root_certificate(cert) + .build() + .unwrap_or_else(|e| panic!("error while creating TLS connector: {}", e)); + let mut http_connector = hyper::client::HttpConnector::new(); + http_connector.enforce_http(false); + hyper_tls::HttpsConnector::from((http_connector, tls_connector.into())) +} + +pub fn native_tls_client() -> Client { + let config = Config::builder() + .http_connector(Adapter::builder().build(native_tls_connector())) + .endpoint_url(format!("https://{DEFAULT_DOMAIN}:{DEFAULT_PORT}")) + .build(); + Client::from_conf(config) } diff --git a/examples/pokemon-service-tls/tests/custom_connectors.rs b/examples/pokemon-service-tls/tests/custom_connectors.rs new file mode 100644 index 0000000000..12c82d55cb --- /dev/null +++ b/examples/pokemon-service-tls/tests/custom_connectors.rs @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub mod common; + +use serial_test::serial; + +#[tokio::test] +#[serial] +// This test invokes an operation with a client that can only send HTTP2 requests and whose TLS +// implementation is backed by `rustls`. +async fn test_check_health_http2_rustls_connector() { + let _child = common::run_server().await; + let client = common::client_http2_only(); + + let _check_health = client.check_health().send().await.unwrap(); +} + +#[tokio::test] +#[serial] +// This test invokes an operation with a client whose TLS implementation is backed by `native_tls`. +async fn test_check_health_native_tls_connector() { + let _child = common::run_server().await; + let client = common::native_tls_client(); + + let _check_health = client.check_health().send().await.unwrap(); +} diff --git a/examples/pokemon-service-tls/tests/http2.rs b/examples/pokemon-service-tls/tests/http2.rs deleted file mode 100644 index 32c0fba08c..0000000000 --- a/examples/pokemon-service-tls/tests/http2.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -pub mod common; - -#[tokio::test] -async fn test_check_health_http2() { - let _child = common::run_server().await; - let client = common::client_http2_only(); - - let _check_health = client.check_health().send().await.unwrap(); -} diff --git a/examples/pokemon-service/Cargo.toml b/examples/pokemon-service/Cargo.toml index 30839509e5..d3bc81ea0b 100644 --- a/examples/pokemon-service/Cargo.toml +++ b/examples/pokemon-service/Cargo.toml @@ -8,7 +8,7 @@ description = "A smithy Rust service to retrieve information about Pokémon." [dependencies] clap = { version = "4.1.11", features = ["derive"] } -hyper = {version = "0.14.25", features = ["server"] } +hyper = { version = "0.14.26", features = ["server"] } tokio = "1.26.0" tower = "0.4" tracing = "0.1" @@ -24,8 +24,11 @@ async-stream = "0.3" rand = "0.8.5" serial_test = "1.0.0" +# We use hyper client in tests +hyper = { version = "0.14.26", features = ["server", "client"] } + # This dependency is only required for testing the `pokemon-service-tls` program. -hyper-rustls = { version = "0.23.2", features = ["http2"] } +hyper-rustls = { version = "0.24", features = ["http2"] } # Local paths aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client/", features = ["rustls"] } diff --git a/examples/pokemon-service/src/main.rs b/examples/pokemon-service/src/main.rs index f26e6d14ba..227d778fc8 100644 --- a/examples/pokemon-service/src/main.rs +++ b/examples/pokemon-service/src/main.rs @@ -8,19 +8,26 @@ mod plugin; use std::{net::SocketAddr, sync::Arc}; use aws_smithy_http_server::{ - extension::OperationExtensionExt, instrumentation::InstrumentExt, plugin::PluginPipeline, - request::request_id::ServerRequestIdProviderLayer, AddExtensionLayer, + extension::OperationExtensionExt, + instrumentation::InstrumentExt, + layer::alb_health_check::AlbHealthCheckLayer, + plugin::{HttpPlugins, IdentityPlugin, Scoped}, + request::request_id::ServerRequestIdProviderLayer, + AddExtensionLayer, }; use clap::Parser; +use hyper::StatusCode; use plugin::PrintExt; + use pokemon_service::{ do_nothing_but_log_request_ids, get_storage_with_local_approved, DEFAULT_ADDRESS, DEFAULT_PORT, }; use pokemon_service_common::{ - capture_pokemon, check_health, get_pokemon_species, get_server_statistics, setup_tracing, State, + capture_pokemon, check_health, get_pokemon_species, get_server_statistics, setup_tracing, + stream_pokemon_radio, State, }; -use pokemon_service_server_sdk::PokemonService; +use pokemon_service_server_sdk::{scope, PokemonService}; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -38,16 +45,26 @@ pub async fn main() { let args = Args::parse(); setup_tracing(); - let plugins = PluginPipeline::new() - // Apply the `PrintPlugin` defined in `plugin.rs` - .print() + scope! { + /// A scope containing `GetPokemonSpecies` and `GetStorage` + struct PrintScope { + includes: [GetPokemonSpecies, GetStorage] + } + } + // Scope the `PrintPlugin`, defined in `plugin.rs`, to `PrintScope` + let print_plugin = Scoped::new::(HttpPlugins::new().print()); + + let plugins = HttpPlugins::new() + // Apply the scoped `PrintPlugin` + .push(print_plugin) // Apply the `OperationExtensionPlugin` defined in `aws_smithy_http_server::extension`. This allows other // plugins or tests to access a `aws_smithy_http_server::extension::OperationExtension` from // `Response::extensions`, or infer routing failure when it's missing. .insert_operation_extension() // Adds `tracing` spans and events to the request lifecycle. .instrument(); - let app = PokemonService::builder_with_plugins(plugins) + + let app = PokemonService::builder_with_plugins(plugins, IdentityPlugin) // Build a registry containing implementations to all the operations in the service. These // are async functions or async closures that take as input the operation's input and // return the operation's output. @@ -57,13 +74,18 @@ pub async fn main() { .capture_pokemon(capture_pokemon) .do_nothing(do_nothing_but_log_request_ids) .check_health(check_health) + .stream_pokemon_radio(stream_pokemon_radio) .build() .expect("failed to build an instance of PokemonService"); let app = app // Setup shared state and middlewares. .layer(&AddExtensionLayer::new(Arc::new(State::default()))) - // Add request IDs + // Handle `/ping` health check requests. + .layer(&AlbHealthCheckLayer::from_handler("/ping", |_req| async { + StatusCode::OK + })) + // Add server request IDs. .layer(&ServerRequestIdProviderLayer::new()); // Using `into_make_service_with_connect_info`, rather than `into_make_service`, to adjoin the `SocketAddr` diff --git a/examples/pokemon-service/src/plugin.rs b/examples/pokemon-service/src/plugin.rs index 8ce0e50d09..971aebb015 100644 --- a/examples/pokemon-service/src/plugin.rs +++ b/examples/pokemon-service/src/plugin.rs @@ -6,10 +6,12 @@ //! Provides an example [`Plugin`] implementation - [`PrintPlugin`]. use aws_smithy_http_server::{ - operation::{Operation, OperationShape}, - plugin::{Plugin, PluginPipeline, PluginStack}, + operation::OperationShape, + plugin::{HttpMarker, HttpPlugins, Plugin, PluginStack}, + service::ServiceShape, + shape_id::ShapeId, }; -use tower::{layer::util::Stack, Layer, Service}; +use tower::Service; use std::task::{Context, Poll}; @@ -17,7 +19,8 @@ use std::task::{Context, Poll}; #[derive(Clone, Debug)] pub struct PrintService { inner: S, - name: &'static str, + operation_id: ShapeId, + service_id: ShapeId, } impl Service for PrintService @@ -33,53 +36,46 @@ where } fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.name); + println!( + "Hi {} in {}", + self.operation_id.absolute(), + self.service_id.absolute() + ); self.inner.call(req) } } - -/// A [`Layer`] which constructs the [`PrintService`]. -#[derive(Debug)] -pub struct PrintLayer { - name: &'static str, -} -impl Layer for PrintLayer { - type Service = PrintService; - - fn layer(&self, service: S) -> Self::Service { - PrintService { - inner: service, - name: self.name, - } - } -} - /// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. #[derive(Debug)] pub struct PrintPlugin; -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin where + Ser: ServiceShape, Op: OperationShape, { - type Service = S; - type Layer = Stack; + type Output = PrintService; - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: Op::NAME }) + fn apply(&self, inner: T) -> Self::Output { + PrintService { + inner, + operation_id: Op::ID, + service_id: Ser::ID, + } } } -/// This provides a [`print`](PrintExt::print) method on [`PluginPipeline`]. -pub trait PrintExt { +impl HttpMarker for PrintPlugin {} + +/// This provides a [`print`](PrintExt::print) method on [`HttpPlugins`]. +pub trait PrintExt { /// Causes all operations to print the operation name when called. /// /// This works by applying the [`PrintPlugin`]. - fn print(self) -> PluginPipeline>; + fn print(self) -> HttpPlugins>; } -impl PrintExt for PluginPipeline { - fn print(self) -> PluginPipeline> { +impl PrintExt for HttpPlugins { + fn print(self) -> HttpPlugins> { self.push(PrintPlugin) } } diff --git a/examples/pokemon-service/tests/common/mod.rs b/examples/pokemon-service/tests/common/mod.rs index b21eb2a78b..2d58f4a975 100644 --- a/examples/pokemon-service/tests/common/mod.rs +++ b/examples/pokemon-service/tests/common/mod.rs @@ -6,30 +6,28 @@ use std::{process::Command, time::Duration}; use assert_cmd::prelude::*; -use aws_smithy_client::erase::{DynConnector, DynMiddleware}; use tokio::time::sleep; use pokemon_service::{DEFAULT_ADDRESS, DEFAULT_PORT}; -use pokemon_service_client::{Builder, Client, Config}; -use pokemon_service_common::{rewrite_base_url, ChildDrop}; +use pokemon_service_client::{Client, Config}; +use pokemon_service_common::ChildDrop; pub async fn run_server() -> ChildDrop { - let child = Command::cargo_bin("pokemon-service") - .unwrap() - .spawn() - .unwrap(); + let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); + let child = Command::cargo_bin(crate_name).unwrap().spawn().unwrap(); sleep(Duration::from_millis(500)).await; ChildDrop(child) } -pub fn client() -> Client> { - let base_url = format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}"); - let raw_client = Builder::new() - .rustls_connector(Default::default()) - .middleware_fn(rewrite_base_url(base_url)) - .build_dyn(); - let config = Config::builder().build(); - Client::with_config(raw_client, config) +pub fn base_url() -> String { + format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}") +} + +pub fn client() -> Client { + let config = Config::builder() + .endpoint_url(format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}")) + .build(); + Client::from_conf(config) } diff --git a/examples/pokemon-service/tests/simple.rs b/examples/pokemon-service/tests/simple.rs index 1c86d3cf10..3a055da587 100644 --- a/examples/pokemon-service/tests/simple.rs +++ b/examples/pokemon-service/tests/simple.rs @@ -81,4 +81,39 @@ async fn simple_integration_test() { let service_statistics_out = client.get_server_statistics().send().await.unwrap(); assert_eq!(2, service_statistics_out.calls_count.unwrap()); + + let hyper_client = hyper::Client::new(); + let health_check_url = format!("{}/ping", common::base_url()); + let health_check_url = hyper::Uri::try_from(health_check_url).unwrap(); + let result = hyper_client.get(health_check_url).await.unwrap(); + + assert_eq!(result.status(), 200); +} + +#[tokio::test] +#[serial] +async fn health_check() { + let _child = common::run_server().await; + + use pokemon_service::{DEFAULT_ADDRESS, DEFAULT_PORT}; + let url = format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}/ping"); + let uri = url.parse::().expect("invalid URL"); + + // Since the `/ping` route is not modeled in Smithy, we use a regular + // Hyper HTTP client to make a request to it. + let request = hyper::Request::builder() + .uri(uri) + .body(hyper::Body::empty()) + .expect("failed to build request"); + + let response = hyper::Client::new() + .request(request) + .await + .expect("failed to get response"); + + assert_eq!(response.status(), hyper::StatusCode::OK); + let body = hyper::body::to_bytes(response.into_body()) + .await + .expect("failed to read response body"); + assert!(body.is_empty()); } diff --git a/rust-runtime/aws-smithy-http-server-python/examples/.gitignore b/examples/python/.gitignore similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/.gitignore rename to examples/python/.gitignore diff --git a/rust-runtime/aws-smithy-http-server-python/examples/Cargo.toml b/examples/python/Cargo.toml similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/Cargo.toml rename to examples/python/Cargo.toml diff --git a/rust-runtime/aws-smithy-http-server-python/examples/Makefile b/examples/python/Makefile similarity index 86% rename from rust-runtime/aws-smithy-http-server-python/examples/Makefile rename to examples/python/Makefile index 3b73f08ca6..74c63f05a8 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/Makefile +++ b/examples/python/Makefile @@ -35,25 +35,26 @@ install-wheel: find $(WHEELS) -type f -name '*.whl' | xargs python3 -m pip install --user --force-reinstall generate-stubs: - python3 $(CUR_DIR)/stubgen.py pokemon_service_server_sdk $(SERVER_SDK_DST)/python/pokemon_service_server_sdk + bash $(SERVER_SDK_DST)/stubgen.sh pokemon_service_server_sdk $(SERVER_SDK_DST)/Cargo.toml $(SERVER_SDK_DST)/python/pokemon_service_server_sdk build: codegen + $(MAKE) generate-stubs $(MAKE) build-wheel - $(MAKE) install-wheel + +release: codegen $(MAKE) generate-stubs $(MAKE) build-wheel-release - $(MAKE) install-wheel -run: build +run: build install-wheel python3 $(CUR_DIR)/pokemon_service.py -py-check: build - python3 -m mypy pokemon_service.py +run-release: release install-wheel + python3 $(CUR_DIR)/pokemon_service.py -py-test: - python3 stubgen_test.py +py-check: install-wheel + python3 -m mypy pokemon_service.py -test: build py-check py-test +test: build py-check cargo test clippy: codegen diff --git a/examples/python/README.md b/examples/python/README.md new file mode 100644 index 0000000000..e3fd43929a --- /dev/null +++ b/examples/python/README.md @@ -0,0 +1,157 @@ +# Smithy Rust/Python Server SDK example + +This folder contains an example service called Pokémon Service used to showcase +the service framework Python bindings capabilities and to run benchmarks. + +The Python implementation of the service can be found inside +[pokemon_service.py](./pokemon_service.py). + +* [Build](#build) + * [Build dependencies](#build-dependencies) + * [Makefile](#makefile) + * [Python stub generation](#python-stub-generation) +* [Run](#run) +* [Test](#test) +* [MacOs](#macos) +* [Running servers on AWS Lambda](#running-servers-on-aws-lambda) + +## Build + +Since this example requires both the server and client SDK to be code-generated +from their [model](/codegen-server-test/model/pokemon.smithy), a Makefile is +provided to build and run the service. Just run `make build` to prepare the first +build. + +### Build dependencies + +Ensure these dependencies are installed. + +```bash +pip install maturin uvloop aiohttp mypy +``` + +The server can depend on [uvloop](https://pypi.org/project/uvloop/) for a faster +event loop implementation and it will be automatically used instead of the standard +library event loop if it is found in the dependencies' closure. + +### Makefile + +The build logic is drive by the Makefile: + +* `make codegen`: run the codegenerator. +* `make build`: build the Maturin package in debug mode (includes type stubs + generation). +* `make release`: build the Maturin package in release mode (includes type stubs + generation). +* `make install-wheel`: install the latest release or debug wheel using `pip`. +* `make run`: run the example server. +* `make test`: run the end-to-end integration test. +* `make clean`: clean the Cargo artefacts. +* `make dist-clean`: clean the Cargo artefacts and generated folders. + +### Python stub generation + +We support the generation of mypy python stubs and every SDK crate ships with +a script called `stubgen.sh`. **Note that the script is not called +automatically as part of the build**. We suggest users to call it after code generation. +It will do first compilation of the crate, generate the types and exit. + +The script takes some command line arguments: + +```bash +./stubgen.sh module_name manifest_path output_directory +``` + +* module_name: name of the Python module to generate stubs for, IE `pokemon_service_server_sdk`. +* manifest_path: path for the crate manifest used to build the types. +* output_directory: directory where to generate the stub hierarchy. **This + directory should be a folder `python/$module_name` in the root of the Maturin package.** + +## Run + +`make run` can be used to start the Pokémon service on `http://localhost:13734`. + +## Test + +`make test` can be used to spawn the Python service and run some simple integration +tests against it. + +More info can be found in the `tests` folder of `pokemon-service-test` package. + +## MacOs + +To compile and test on MacOs, please follow the official PyO3 guidelines on how +to [configure your linker](https://pyo3.rs/latest/building_and_distribution.html?highlight=rustflags#macos). + +Please note that the `.cargo/config.toml` with linkers override can be local to +your project. + +## Running servers on AWS Lambda + +`aws-smithy-http-server-python` supports running your services on [AWS Lambda](https://aws.amazon.com/lambda/). + +You need to use `run_lambda` method instead of `run` method to start +the [custom runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) +instead of the [Hyper](https://hyper.rs/) HTTP server. + +In your `app.py`: + +```diff +from pokemon_service_server_sdk import App +from pokemon_service_server_sdk.error import ResourceNotFoundException + +# ... + +# Get the number of requests served by this server. +@app.get_server_statistics +def get_server_statistics( + _: GetServerStatisticsInput, context: Context +) -> GetServerStatisticsOutput: + calls_count = context.get_calls_count() + logging.debug("The service handled %d requests", calls_count) + return GetServerStatisticsOutput(calls_count=calls_count) + +# ... + +-app.run() ++app.run_lambda() +``` + +`aws-smithy-http-server-python` comes with a +[custom runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) +so you should run your service without any provided runtimes. +You can achieve that with a `Dockerfile` similar to this: + +```dockerfile +# You can use any image that has your desired Python version +FROM public.ecr.aws/lambda/python:3.8-x86_64 + +# Copy your application code to `LAMBDA_TASK_ROOT` +COPY app.py ${LAMBDA_TASK_ROOT} + +# When you build your Server SDK for your service, you will get a Python wheel. +# You just need to copy that wheel and install it via `pip` inside your image. +# Note that you need to build your library for Linux, and Python version used to +# build your SDK should match with your image's Python version. +# For cross compiling, you can consult to: +# https://pyo3.rs/latest/building_and_distribution.html#cross-compiling +COPY wheels/ ${LAMBDA_TASK_ROOT}/wheels +RUN pip3 install ${LAMBDA_TASK_ROOT}/wheels/*.whl + +# You can install your application's other dependencies listed in `requirements.txt`. +COPY requirements.txt . +RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" + +# Create a symlink for your application's entrypoint, +# so we can use `/app.py` to refer it +RUN ln -s ${LAMBDA_TASK_ROOT}/app.py /app.py + +# By default `public.ecr.aws/lambda/python` images comes with Python runtime, +# we need to override `ENTRYPOINT` and `CMD` to not call that runtime and +# instead run directly your service and it will start our custom runtime. +ENTRYPOINT [ "/var/lang/bin/python3.8" ] +CMD [ "/app.py" ] +``` + +See [https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-create-from-base](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-create-from-base) +for more details on building your custom image. diff --git a/rust-runtime/aws-smithy-http-server-python/examples/mypy.ini b/examples/python/mypy.ini similarity index 73% rename from rust-runtime/aws-smithy-http-server-python/examples/mypy.ini rename to examples/python/mypy.ini index c6a867948d..7b6f4e1690 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/mypy.ini +++ b/examples/python/mypy.ini @@ -3,3 +3,6 @@ [mypy] strict = True + +[mypy-aiohttp.*] +ignore_missing_imports = True diff --git a/examples/python/pokemon-service-test/Cargo.toml b/examples/python/pokemon-service-test/Cargo.toml new file mode 100644 index 0000000000..b4084185c2 --- /dev/null +++ b/examples/python/pokemon-service-test/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "pokemon-service-test" +version = "0.1.0" +edition = "2021" +publish = false +authors = ["Smithy-rs Server Team "] +description = "Run tests against the Python server implementation" + +[dev-dependencies] +rand = "0.8" +async-stream = "0.3" +command-group = "2.1.0" +tokio = { version = "1.20.1", features = ["full"] } +serial_test = "2.0.0" +rustls-pemfile = "1.0.1" +tokio-rustls = "0.24.0" +hyper-rustls = { version = "0.24", features = ["http2"] } + +# Local paths +aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client/", features = ["rustls"] } +aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http/" } +aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types/" } +pokemon-service-client = { path = "../pokemon-service-client/" } diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/helpers.rs b/examples/python/pokemon-service-test/tests/helpers.rs similarity index 74% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/helpers.rs rename to examples/python/pokemon-service-test/tests/helpers.rs index 23050a3da3..708e17fe88 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/helpers.rs +++ b/examples/python/pokemon-service-test/tests/helpers.rs @@ -8,19 +8,15 @@ use std::io::BufReader; use std::process::Command; use std::time::Duration; -use aws_smithy_client::{erase::DynConnector, hyper_ext::Adapter}; -use aws_smithy_http::operation::Request; +use aws_smithy_client::hyper_ext::Adapter; use command_group::{CommandGroup, GroupChild}; -use pokemon_service_client::{Builder, Client, Config}; +use pokemon_service_client::{Client, Config}; use tokio::time; const TEST_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testdata/localhost.key"); const TEST_CERT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testdata/localhost.crt"); -pub type PokemonClient = Client< - aws_smithy_client::erase::DynConnector, - aws_smithy_client::erase::DynMiddleware, ->; +pub type PokemonClient = Client; enum PokemonServiceVariant { Http, @@ -94,12 +90,8 @@ impl Drop for PokemonService { #[allow(dead_code)] pub fn client() -> PokemonClient { let base_url = PokemonServiceVariant::Http.base_url(); - let raw_client = Builder::new() - .rustls_connector(Default::default()) - .middleware_fn(rewrite_base_url(base_url)) - .build_dyn(); - let config = Config::builder().build(); - Client::with_config(raw_client, config) + let config = Config::builder().endpoint_url(base_url).build(); + Client::from_conf(config) } #[allow(dead_code)] @@ -122,19 +114,9 @@ pub fn http2_client() -> PokemonClient { .build(); let base_url = PokemonServiceVariant::Http2.base_url(); - let raw_client = Builder::new() - .connector(DynConnector::new(Adapter::builder().build(connector))) - .middleware_fn(rewrite_base_url(base_url)) - .build_dyn(); - let config = Config::builder().build(); - Client::with_config(raw_client, config) -} - -fn rewrite_base_url(base_url: &'static str) -> impl Fn(Request) -> Request + Clone { - move |mut req| { - let http_req = req.http_mut(); - let uri = format!("{base_url}{}", http_req.uri().path()); - *http_req.uri_mut() = uri.parse().unwrap(); - req - } + let config = Config::builder() + .http_connector(Adapter::builder().build(connector)) + .endpoint_url(base_url) + .build(); + Client::from_conf(config) } diff --git a/examples/python/pokemon-service-test/tests/simple_integration_test.rs b/examples/python/pokemon-service-test/tests/simple_integration_test.rs new file mode 100644 index 0000000000..39f979e9a3 --- /dev/null +++ b/examples/python/pokemon-service-test/tests/simple_integration_test.rs @@ -0,0 +1,216 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// Files here are for running integration tests. +// These tests only have access to your crate's public API. +// See: https://doc.rust-lang.org/book/ch11-03-test-organization.html#integration-tests + +use async_stream::stream; +use aws_smithy_types::error::display::DisplayErrorContext; +use rand::Rng; +use serial_test::serial; + +use crate::helpers::{client, http2_client, PokemonClient, PokemonService}; +use pokemon_service_client::{ + types::error::{AttemptCapturingPokemonEventError, MasterBallUnsuccessful}, + types::{AttemptCapturingPokemonEvent, CapturingEvent, CapturingPayload}, +}; + +mod helpers; + +#[tokio::test] +#[serial] +async fn simple_integration_test() { + let _program = PokemonService::run().await; + simple_integration_test_with_client(client()).await; +} + +#[tokio::test] +#[serial] +async fn simple_integration_test_http2() { + let _program = PokemonService::run_http2().await; + simple_integration_test_with_client(http2_client()).await; +} + +async fn simple_integration_test_with_client(client: PokemonClient) { + let service_statistics_out = client.get_server_statistics().send().await.unwrap(); + assert_eq!(0, service_statistics_out.calls_count.unwrap()); + + let pokemon_species_output = client + .get_pokemon_species() + .name("pikachu") + .send() + .await + .unwrap(); + assert_eq!("pikachu", pokemon_species_output.name().unwrap()); + + let service_statistics_out = client.get_server_statistics().send().await.unwrap(); + assert_eq!(1, service_statistics_out.calls_count.unwrap()); + + let pokemon_species_error = client + .get_pokemon_species() + .name("some_pokémon") + .send() + .await + .unwrap_err(); + let message = DisplayErrorContext(pokemon_species_error).to_string(); + let expected = + r#"ResourceNotFoundError [ResourceNotFoundException]: Requested Pokémon not available"#; + assert!( + message.contains(expected), + "expected '{message}' to contain '{expected}'" + ); + + let service_statistics_out = client.get_server_statistics().send().await.unwrap(); + assert_eq!(2, service_statistics_out.calls_count.unwrap()); + + let _health_check = client.check_health().send().await.unwrap(); +} + +#[tokio::test] +#[serial] +async fn event_stream_test() { + let _program = PokemonService::run().await; + + let mut team = vec![]; + let input_stream = stream! { + // Always Pikachu + yield Ok(AttemptCapturingPokemonEvent::Event( + CapturingEvent::builder() + .payload(CapturingPayload::builder() + .name("Pikachu") + .pokeball("Master Ball") + .build()) + .build() + )); + yield Ok(AttemptCapturingPokemonEvent::Event( + CapturingEvent::builder() + .payload(CapturingPayload::builder() + .name("Regieleki") + .pokeball("Fast Ball") + .build()) + .build() + )); + yield Err(AttemptCapturingPokemonEventError::MasterBallUnsuccessful(MasterBallUnsuccessful::builder().build())); + // The next event should not happen + yield Ok(AttemptCapturingPokemonEvent::Event( + CapturingEvent::builder() + .payload(CapturingPayload::builder() + .name("Charizard") + .pokeball("Great Ball") + .build()) + .build() + )); + }; + + // Throw many! + let mut output = client() + .capture_pokemon() + .region("Kanto") + .events(input_stream.into()) + .send() + .await + .unwrap(); + loop { + match output.events.recv().await { + Ok(Some(capture)) => { + let pokemon = capture.as_event().unwrap().name.as_ref().unwrap().clone(); + let pokedex = capture + .as_event() + .unwrap() + .pokedex_update + .as_ref() + .unwrap() + .clone(); + let shiny = if *capture.as_event().unwrap().shiny.as_ref().unwrap() { + "" + } else { + "not " + }; + let expected_pokedex: Vec = (0..255).collect(); + println!("captured {} ({}shiny)", pokemon, shiny); + if expected_pokedex == pokedex.into_inner() { + println!("pokedex updated") + } + team.push(pokemon); + } + Err(e) => { + println!("error from the server: {:?}", e); + break; + } + Ok(None) => break, + } + } + + while team.len() < 6 { + let pokeball = get_pokeball(); + let pokemon = get_pokemon_to_capture(); + let input_stream = stream! { + yield Ok(AttemptCapturingPokemonEvent::Event( + CapturingEvent::builder() + .payload(CapturingPayload::builder() + .name(pokemon) + .pokeball(pokeball) + .build()) + .build() + )) + }; + let mut output = client() + .capture_pokemon() + .region("Kanto") + .events(input_stream.into()) + .send() + .await + .unwrap(); + match output.events.recv().await { + Ok(Some(capture)) => { + let pokemon = capture.as_event().unwrap().name.as_ref().unwrap().clone(); + let pokedex = capture + .as_event() + .unwrap() + .pokedex_update + .as_ref() + .unwrap() + .clone(); + let shiny = if *capture.as_event().unwrap().shiny.as_ref().unwrap() { + "" + } else { + "not " + }; + let expected_pokedex: Vec = (0..255).collect(); + println!("captured {} ({}shiny)", pokemon, shiny); + if expected_pokedex == pokedex.into_inner() { + println!("pokedex updated") + } + team.push(pokemon); + } + Err(e) => { + println!("error from the server: {:?}", e); + break; + } + Ok(None) => {} + } + } + println!("Team: {:?}", team); +} + +fn get_pokeball() -> String { + let random = rand::thread_rng().gen_range(0..100); + let pokeball = if random < 5 { + "Master Ball" + } else if random < 30 { + "Great Ball" + } else if random < 80 { + "Fast Ball" + } else { + "Smithy Ball" + }; + pokeball.to_string() +} + +fn get_pokemon_to_capture() -> String { + let pokemons = vec!["Charizard", "Pikachu", "Regieleki"]; + pokemons[rand::thread_rng().gen_range(0..pokemons.len())].to_string() +} diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/testdata/localhost.crt b/examples/python/pokemon-service-test/tests/testdata/localhost.crt similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/testdata/localhost.crt rename to examples/python/pokemon-service-test/tests/testdata/localhost.crt diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/testdata/localhost.key b/examples/python/pokemon-service-test/tests/testdata/localhost.key similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/testdata/localhost.key rename to examples/python/pokemon-service-test/tests/testdata/localhost.key diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py b/examples/python/pokemon_service.py similarity index 52% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py rename to examples/python/pokemon_service.py index a3c7bf1c93..552d892ff3 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py +++ b/examples/python/pokemon_service.py @@ -6,40 +6,58 @@ import argparse import logging import random -from threading import Lock from dataclasses import dataclass -from typing import Dict, Any, List, Optional, Callable, Awaitable +from threading import Lock +from typing import Any, AsyncIterator, Awaitable, Callable, Dict, List, Optional from pokemon_service_server_sdk import App -from pokemon_service_server_sdk.tls import TlsConfig from pokemon_service_server_sdk.aws_lambda import LambdaContext -from pokemon_service_server_sdk.error import ResourceNotFoundException +from pokemon_service_server_sdk.error import ( + InvalidPokeballError, + MasterBallUnsuccessful, + ResourceNotFoundException, + UnsupportedRegionError, + StorageAccessNotAuthorized, +) from pokemon_service_server_sdk.input import ( + GetStorageInput, + CapturePokemonInput, + CheckHealthInput, DoNothingInput, GetPokemonSpeciesInput, GetServerStatisticsInput, - CheckHealthInput, StreamPokemonRadioInput, ) from pokemon_service_server_sdk.logging import TracingHandler -from pokemon_service_server_sdk.middleware import ( - MiddlewareException, - Response, - Request, +from pokemon_service_server_sdk.middleware import MiddlewareException, Request, Response +from pokemon_service_server_sdk.model import ( + CaptureEvent, + CapturePokemonEvents, + FlavorText, + Language, ) -from pokemon_service_server_sdk.model import FlavorText, Language from pokemon_service_server_sdk.output import ( + GetStorageOutput, + CapturePokemonOutput, + CheckHealthOutput, DoNothingOutput, GetPokemonSpeciesOutput, GetServerStatisticsOutput, - CheckHealthOutput, StreamPokemonRadioOutput, ) +from pokemon_service_server_sdk.tls import TlsConfig from pokemon_service_server_sdk.types import ByteStream # Logging can bee setup using standard Python tooling. We provide # fast logging handler, Tracingandler based on Rust tracing crate. -logging.basicConfig(handlers=[TracingHandler(level=logging.DEBUG).handler()]) +logging.basicConfig( + handlers=[ + TracingHandler( + level=logging.DEBUG, + format="pretty", # You can also use "json" or "compact" (default) + ).handler() + ] +) class SafeCounter: @@ -152,16 +170,19 @@ def get_random_radio_stream(self) -> str: # Next is either the next middleware in the stack or the handler. Next = Callable[[Request], Awaitable[Response]] + # This middleware checks the `Content-Type` from the request header, # logs some information depending on that and then calls `next`. @app.middleware async def check_content_type_header(request: Request, next: Next) -> Response: content_type = request.headers.get("content-type") if content_type == "application/json": - logging.debug("Found valid `application/json` content type") + logging.debug("found valid `application/json` content type") else: logging.warning( - f"Invalid content type {content_type}, dumping headers: {request.headers.items()}" + "invalid content type %s, dumping headers: %s", + content_type, + request.headers.items(), ) return await next(request) @@ -172,7 +193,7 @@ async def check_content_type_header(request: Request, next: Next) -> Response: @app.middleware async def add_x_amzn_answer_header(request: Request, next: Next) -> Response: request.headers["x-amzn-answer"] = "42" - logging.debug("Setting `x-amzn-answer` header to 42") + logging.debug("setting `x-amzn-answer` header to 42") return await next(request) @@ -193,10 +214,22 @@ async def check_x_amzn_answer_header(request: Request, next: Next) -> Response: # DoNothing operation used for raw benchmarking. @app.do_nothing def do_nothing(_: DoNothingInput) -> DoNothingOutput: - # logging.debug("Running the DoNothing operation") return DoNothingOutput() +# Retrieves the user's storage. +@app.get_storage +def get_storage(input: GetStorageInput) -> GetStorageOutput: + logging.debug("attempting to authenticate storage user") + + # We currently only support Ash and he has nothing stored + if input.user != "ash" or input.passcode != "pikachu123": + logging.debug("authentication failed") + raise StorageAccessNotAuthorized() + + return GetStorageOutput([]) + + # Get the translation of a Pokémon specie or an error. @app.get_pokemon_species def get_pokemon_species( @@ -204,7 +237,7 @@ def get_pokemon_species( ) -> GetPokemonSpeciesOutput: if context.lambda_ctx is not None: logging.debug( - "Lambda Context: %s", + "lambda Context: %s", dict( request_id=context.lambda_ctx.request_id, deadline=context.lambda_ctx.deadline, @@ -217,14 +250,13 @@ def get_pokemon_species( context.increment_calls_count() flavor_text_entries = context.get_pokemon_description(input.name) if flavor_text_entries: - logging.debug("Total requests executed: %s", context.get_calls_count()) - logging.info("Found description for Pokémon %s", input.name) - logging.error("Found some stuff") + logging.debug("total requests executed: %s", context.get_calls_count()) + logging.info("found description for Pokémon %s", input.name) return GetPokemonSpeciesOutput( name=input.name, flavor_text_entries=flavor_text_entries ) else: - logging.warning("Description for Pokémon %s not in the database", input.name) + logging.warning("description for Pokémon %s not in the database", input.name) raise ResourceNotFoundException("Requested Pokémon not available") @@ -234,7 +266,7 @@ def get_server_statistics( _: GetServerStatisticsInput, context: Context ) -> GetServerStatisticsOutput: calls_count = context.get_calls_count() - logging.debug("The service handled %d requests", calls_count) + logging.debug("the service handled %d requests", calls_count) return GetServerStatisticsOutput(calls_count=calls_count) @@ -244,19 +276,172 @@ def check_health(_: CheckHealthInput) -> CheckHealthOutput: return CheckHealthOutput() +########################################################### +# Event streams +############################################################ +# An event stream is an abstraction that allows multiple messages to be sent asynchronously +# between a client and server. Event streams support both duplex and simplex streaming. +# You can find more details about event streaming in Smithy Spec: +# https://smithy.io/2.0/spec/streaming.html#event-streams +# +# Event streams modeled as asynchronous Python generators, that means: +# For receiving, you can use `async for` to iterate incoming events from the client and you can +# `break` from the loop if you want to stop receiving events. +# For sending, you can use `yield` to sent an event to the client and you can `return` from your +# generator function to stop `yield`ing events. +# +# Event streams also has a concept of "Modeled Errors" and those events are considered as +# terminal errors and they stop the event stream. They are modeled as exceptions in Python. +# Incoming event streams can raise modeled exceptions to terminate the event stream and you can +# catch those errors by wrapping incoming streams in a `try-except` block, and you can also raise +# a modeled error to terminate event stream. +# See Smithy Spec for more information about modeled errors: +# https://smithy.io/2.0/spec/streaming.html#modeled-errors-in-event-streams +# +# Depending on your use case, your functions usually should look like: +# +# Receiving only: +# ```python +# def receiving(input: Input) -> Output: +# # Initial message handling... +# +# async def events(input): +# try: +# async for event in input.events: +# # Handle incoming `event`... +# # `input.events` will gracefully stop if client stops sending events, +# # you can also `break` if you want to stop receiving +# except YourModeledError as err: +# # Handle modeled error... +# # The stream is terminated +# +# # Return immediately and let your generator run in background, +# # you can also have initial messages and send them here +# return Output(events=events(input), initial="value") +# ``` +# +# Sending only: +# ```python +# def sending(input: Input) -> Output: +# # Initial message handling... +# +# async def events(input): +# while True: +# # You can send values by `yield`ing them... +# yield some_value +# +# # You can just `break` the loop to stop sending events gracefully +# if some_cond: +# break +# +# # You can also stop sending events by raising modeled errors +# if some_other_cond: +# raise YourModeledError(...) +# +# # Return immediately and let your generator run in background, +# # you can also have initial messages and send them here +# return Output(events=events(input), initial="value") +# ``` +# +# Both receiving and sending: +# ```python +# def bidirectional(input: Input) -> Output: +# # Initial message handling... +# +# async def events(input): +# try: +# async for event in input.events: +# # Handle incoming `event`... +# # `input.events` will gracefully stop if client stops sending events, +# # you can also `break` if you want to stop receiving +# +# # Send some event to the client by `yield`ing them, or stop sending events +# # by `break`ing the loop or raising modeled errors +# yield outgoing +# except YourModeledError as err: +# # Handle modeled error... +# # The stream is terminated +# +# # Return immediately and let your generator run in background, +# # you can also have initial messages and send them here +# return Output(events=events(input), initial="value") +# ``` +# +# You can see an example implementation of a duplex streaming for capturing a Pokemon, +# you can find Smithy model in `codegen-server-test/python/model/pokemon.smithy`. +@app.capture_pokemon +def capture_pokemon(input: CapturePokemonInput) -> CapturePokemonOutput: + # You can have initial messages that provides an opportunity for a client or server to + # provide metadata about an event stream before transmitting events. + # See Smithy spec for more details: https://smithy.io/2.0/spec/streaming.html#initial-messages + if input.region != "Kanto": + raise UnsupportedRegionError(input.region) + + # Here is your event stream, which is an `async` Python function that can `await` on incoming + # events and `yield` to sent an event + async def events(input: CapturePokemonInput) -> AsyncIterator[CapturePokemonEvents]: + # We're wrapping incoming event stream with a `try-catch` block to catch modeled errors + try: + # Asynchronously iterate through incoming events + async for incoming in input.events: + # `incoming` is an union of all possible non-error types from your Smithy model. + # You can use `is_{variant}` and `as_{variant}` methods to inspect incoming message + if incoming.is_event(): + event = incoming.as_event() + payload = event.payload + # You can ignore incoming messages just by `continue`ing the loop + if not payload: + logging.debug("no payload provided, ignoring the event") + continue + + name = payload.name or "" + pokeball = payload.pokeball or "" + # You can terminate the stream by raising modeled errors + if pokeball not in ["Master Ball", "Fast Ball"]: + raise InvalidPokeballError(pokeball) + + # Outgoing type is also an union of all possible non-error types from + # your Smithy model. You can construct the variant you want by calling + # constructor functions for your variant: + outgoing_event = CapturePokemonEvents.event( + CaptureEvent( + name=name, + captured=random.choice([True, False]), + shiny=random.choice([True, False]), + ) + ) + # You can `yield` your outgoing type to sent an event to the client + yield outgoing_event + else: + # You can stop receiving incoming events by just `break`ing the loop + logging.error("unknown event") + break + # You can catch modeled errors and act accordingly, they will terminate the event stream + except MasterBallUnsuccessful as err: + logging.error("masterball unsuccessful: %s", err) + + # Here event stream is going to be completed because we stopped receiving events and we'll + # no longer `yield` new values and this asynchronous Python generator will end here + logging.debug("done") + + # You should immediately return your output here, your asynchronous Python generator will + # run in background without blocking your threads. + return CapturePokemonOutput(events=events(input)) + + # Stream a random Pokémon song. @app.stream_pokemon_radio async def stream_pokemon_radio( _: StreamPokemonRadioInput, context: Context ) -> StreamPokemonRadioOutput: - import aiohttp # type: ignore + import aiohttp radio_url = context.get_random_radio_stream() - logging.info("Random radio URL for this stream is %s", radio_url) + logging.info("random radio URL for this stream is %s", radio_url) async with aiohttp.ClientSession() as session: async with session.get(radio_url) as response: data = ByteStream(await response.read()) - logging.debug("Successfully fetched radio url %s", radio_url) + logging.debug("successfully fetched radio url %s", radio_url) return StreamPokemonRadioOutput(data=data) diff --git a/gradle.properties b/gradle.properties index ead4b8d783..4b2eb37857 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ # # Rust MSRV (entered into the generated README) -rust.msrv=1.66.1 +rust.msrv=1.69.0 # To enable debug, swap out the two lines below. # When changing this value, be sure to run `./gradlew --stop` to kill the Gradle daemon. @@ -12,13 +12,13 @@ rust.msrv=1.66.1 org.gradle.jvmargs=-Xmx1024M # Version number to use for the generated runtime crates -smithy.rs.runtime.crate.version=0.0.0-smithy-rs-head +smithy.rs.runtime.crate.version=0.56.0 kotlin.code.style=official # codegen -smithyGradlePluginVersion=0.6.0 -smithyVersion=1.28.1 +smithyGradlePluginVersion=0.7.0 +smithyVersion=1.35.0 # kotlin kotlinVersion=1.7.21 diff --git a/rust-runtime/aws-smithy-async/Cargo.toml b/rust-runtime/aws-smithy-async/Cargo.toml index d148e229e4..c95862d9ff 100644 --- a/rust-runtime/aws-smithy-async/Cargo.toml +++ b/rust-runtime/aws-smithy-async/Cargo.toml @@ -9,6 +9,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] rt-tokio = ["tokio/time"] +test-util = [] [dependencies] pin-project-lite = "0.2" diff --git a/rust-runtime/aws-smithy-async/additional-ci b/rust-runtime/aws-smithy-async/additional-ci index fde542e9fc..2e342db6fa 100755 --- a/rust-runtime/aws-smithy-async/additional-ci +++ b/rust-runtime/aws-smithy-async/additional-ci @@ -8,6 +8,9 @@ set -e +echo '### Checking compilation under WASM' +cargo check --target wasm32-unknown-unknown + echo "### Checking for duplicate dependency versions in the normal dependency graph with all features enabled" cargo tree -d --edges normal --all-features diff --git a/rust-runtime/aws-smithy-async/external-types.toml b/rust-runtime/aws-smithy-async/external-types.toml index 613ae21ffb..424f7dc1db 100644 --- a/rust-runtime/aws-smithy-async/external-types.toml +++ b/rust-runtime/aws-smithy-async/external-types.toml @@ -1,7 +1,8 @@ allowed_external_types = [ + "aws_smithy_types::config_bag::storable::Storable", + "aws_smithy_types::config_bag::storable::StoreReplace", + "aws_smithy_types::config_bag::storable::Storer", + # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Switch to AsyncIterator once standardized "futures_core::stream::Stream", - - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Don't expose `SendError` - "tokio::sync::mpsc::error::SendError", ] diff --git a/rust-runtime/aws-smithy-async/src/future/rendezvous.rs b/rust-runtime/aws-smithy-async/src/future/rendezvous.rs index b501efc85b..16456f123e 100644 --- a/rust-runtime/aws-smithy-async/src/future/rendezvous.rs +++ b/rust-runtime/aws-smithy-async/src/future/rendezvous.rs @@ -14,7 +14,6 @@ use std::sync::Arc; use std::task::{Context, Poll}; -use tokio::sync::mpsc::error::SendError; use tokio::sync::Semaphore; /// Create a new rendezvous channel @@ -38,6 +37,36 @@ pub fn channel() -> (Sender, Receiver) { ) } +/// Errors for rendezvous channel +pub mod error { + use std::fmt; + use tokio::sync::mpsc::error::SendError as TokioSendError; + + /// Error when [crate::future::rendezvous::Sender] fails to send a value to the associated `Receiver` + #[derive(Debug)] + pub struct SendError { + source: TokioSendError, + } + + impl SendError { + pub(crate) fn tokio_send_error(source: TokioSendError) -> Self { + Self { source } + } + } + + impl fmt::Display for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "failed to send value to the receiver") + } + } + + impl std::error::Error for SendError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.source) + } + } +} + #[derive(Debug)] /// Sender-half of a channel pub struct Sender { @@ -50,7 +79,7 @@ impl Sender { /// /// Unlike something like `tokio::sync::mpsc::Channel` where sending a value will be buffered until /// demand exists, a rendezvous sender will wait until matching demand exists before this function will return. - pub async fn send(&self, item: T) -> Result<(), SendError> { + pub async fn send(&self, item: T) -> Result<(), error::SendError> { let result = self.chan.send(item).await; // If this is an error, the rx half has been dropped. We will never get demand. if result.is_ok() { @@ -61,7 +90,7 @@ impl Sender { .expect("semaphore is never closed") .forget(); } - result + result.map_err(error::SendError::tokio_send_error) } } diff --git a/rust-runtime/aws-smithy-async/src/lib.rs b/rust-runtime/aws-smithy-async/src/lib.rs index 7e106da5db..59fdc54e0f 100644 --- a/rust-runtime/aws-smithy-async/src/lib.rs +++ b/rust-runtime/aws-smithy-async/src/lib.rs @@ -18,6 +18,9 @@ pub mod future; pub mod rt; +#[cfg(feature = "test-util")] +pub mod test_util; +pub mod time; /// Given an `Instant` and a `Duration`, assert time elapsed since `Instant` is equal to `Duration`. /// This macro allows for a 5ms margin of error. @@ -37,12 +40,13 @@ macro_rules! assert_elapsed { ($start:expr, $dur:expr, $margin_of_error:expr) => {{ let elapsed = $start.elapsed(); // type ascription improves compiler error when wrong type is passed - let lower: std::time::Duration = $dur; let margin_of_error: std::time::Duration = $margin_of_error; + let lower: std::time::Duration = $dur - margin_of_error; + let upper: std::time::Duration = $dur + margin_of_error; // Handles ms rounding assert!( - elapsed >= lower && elapsed <= lower + margin_of_error, + elapsed >= lower && elapsed <= upper, "actual = {:?}, expected = {:?}", elapsed, lower diff --git a/rust-runtime/aws-smithy-async/src/rt/sleep.rs b/rust-runtime/aws-smithy-async/src/rt/sleep.rs index 9921fd9d0b..076db95c97 100644 --- a/rust-runtime/aws-smithy-async/src/rt/sleep.rs +++ b/rust-runtime/aws-smithy-async/src/rt/sleep.rs @@ -39,20 +39,50 @@ where } } +/// Wrapper type for sharable `AsyncSleep` +#[derive(Clone, Debug)] +pub struct SharedAsyncSleep(Arc); + +impl SharedAsyncSleep { + /// Create a new `SharedAsyncSleep` from `AsyncSleep` + pub fn new(sleep: impl AsyncSleep + 'static) -> Self { + Self(Arc::new(sleep)) + } +} + +impl AsRef for SharedAsyncSleep { + fn as_ref(&self) -> &(dyn AsyncSleep + 'static) { + self.0.as_ref() + } +} + +impl From> for SharedAsyncSleep { + fn from(sleep: Arc) -> Self { + SharedAsyncSleep(sleep) + } +} + +impl AsyncSleep for SharedAsyncSleep { + fn sleep(&self, duration: Duration) -> Sleep { + self.0.sleep(duration) + } +} + #[cfg(feature = "rt-tokio")] /// Returns a default sleep implementation based on the features enabled -pub fn default_async_sleep() -> Option> { - Some(sleep_tokio()) +pub fn default_async_sleep() -> Option { + Some(SharedAsyncSleep::from(sleep_tokio())) } #[cfg(not(feature = "rt-tokio"))] /// Returns a default sleep implementation based on the features enabled -pub fn default_async_sleep() -> Option> { +pub fn default_async_sleep() -> Option { None } /// Future returned by [`AsyncSleep`]. #[non_exhaustive] +#[must_use] pub struct Sleep(Pin + Send + 'static>>); impl Debug for Sleep { diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs new file mode 100644 index 0000000000..e323478d84 --- /dev/null +++ b/rust-runtime/aws-smithy-async/src/test_util.rs @@ -0,0 +1,348 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Test utilities for time and sleep + +use std::sync::{Arc, Mutex}; +use std::time::{Duration, SystemTime}; + +use tokio::sync::oneshot; +use tokio::sync::Barrier; +use tokio::time::timeout; + +use crate::rt::sleep::{AsyncSleep, Sleep}; +use crate::time::{SharedTimeSource, TimeSource}; + +/// Manually controlled time source +#[derive(Debug, Clone)] +pub struct ManualTimeSource { + start_time: SystemTime, + log: Arc>>, +} + +#[cfg(feature = "test-util")] +impl ManualTimeSource { + /// Get the number of seconds since the UNIX Epoch as an f64. + /// + /// ## Panics + /// + /// This will panic if `self.now()` returns a time that's before the UNIX Epoch. + pub fn seconds_since_unix_epoch(&self) -> f64 { + self.now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs_f64() + } + + /// Creates a new [`ManualTimeSource`] + pub fn new(start_time: SystemTime) -> ManualTimeSource { + Self { + start_time, + log: Default::default(), + } + } + + /// Advances the time of this time source by `duration`. + pub fn advance(&self, duration: Duration) -> SystemTime { + let mut log = self.log.lock().unwrap(); + log.push(duration); + self._now(&log) + } + + fn _now(&self, log: &[Duration]) -> SystemTime { + self.start_time + log.iter().sum::() + } + + /// Sets the `time` of this manual time source. + /// + /// # Panics + /// This function panics if `time` < `now()` + pub fn set_time(&self, time: SystemTime) { + let mut log = self.log.lock().unwrap(); + let now = self._now(&log); + if time < now { + panic!("Cannot move time backwards!"); + } + log.push(time.duration_since(now).unwrap()); + } +} + +impl TimeSource for ManualTimeSource { + fn now(&self) -> SystemTime { + self._now(&self.log.lock().unwrap()) + } +} + +/// A sleep implementation where calls to [`AsyncSleep::sleep`] block until [`SleepGate::expect_sleep`] is called +/// +/// Create a [`ControlledSleep`] with [`controlled_time_and_sleep`] +#[derive(Debug, Clone)] +pub struct ControlledSleep { + barrier: Arc, + log: Arc>>, + duration: Arc>>, + advance_guard: Arc>>>, +} + +/// Gate that allows [`ControlledSleep`] to advance. +/// +/// See [`controlled_time_and_sleep`] for more details +pub struct SleepGate { + gate: Arc, + pending: Arc>>, + advance_guard: Arc>>>, +} + +impl ControlledSleep { + fn new(log: Arc>>) -> (ControlledSleep, SleepGate) { + let gate = Arc::new(Barrier::new(2)); + let pending = Arc::new(Mutex::new(None)); + let advance_guard: Arc>>> = Default::default(); + ( + ControlledSleep { + barrier: gate.clone(), + log, + duration: pending.clone(), + advance_guard: advance_guard.clone(), + }, + SleepGate { + gate, + pending, + advance_guard, + }, + ) + } +} + +/// A sleep implementation where calls to [`AsyncSleep::sleep`] will complete instantly. +/// +/// Create a [`InstantSleep`] with [`instant_time_and_sleep`] +#[derive(Debug, Clone)] +pub struct InstantSleep { + log: Arc>>, +} + +impl AsyncSleep for InstantSleep { + fn sleep(&self, duration: Duration) -> Sleep { + let log = self.log.clone(); + Sleep::new(async move { + log.lock().unwrap().push(duration); + }) + } +} + +impl InstantSleep { + /// Given a shared log for sleep durations, create a new `InstantSleep`. + pub fn new(log: Arc>>) -> Self { + Self { log } + } + + /// Return the sleep durations that were logged by this `InstantSleep`. + pub fn logs(&self) -> Vec { + self.log.lock().unwrap().iter().cloned().collect() + } + + /// Return the total sleep duration that was logged by this `InstantSleep`. + pub fn total_duration(&self) -> Duration { + self.log.lock().unwrap().iter().sum() + } +} + +/// Guard returned from [`SleepGate::expect_sleep`] +/// +/// # Examples +/// ```rust +/// # use std::sync::Arc; +/// use std::sync::atomic::{AtomicUsize, Ordering}; +/// # async { +/// use std::time::{Duration, UNIX_EPOCH}; +/// use aws_smithy_async::rt::sleep::AsyncSleep; +/// use aws_smithy_async::test_util::controlled_time_and_sleep; +/// let (time, sleep, mut gate) = controlled_time_and_sleep(UNIX_EPOCH); +/// let progress = Arc::new(AtomicUsize::new(0)); +/// let task_progress = progress.clone(); +/// let task = tokio::spawn(async move { +/// let progress = task_progress; +/// progress.store(1, Ordering::Release); +/// sleep.sleep(Duration::from_secs(1)).await; +/// progress.store(2, Ordering::Release); +/// sleep.sleep(Duration::from_secs(2)).await; +/// }); +/// while progress.load(Ordering::Acquire) != 1 {} +/// let guard = gate.expect_sleep().await; +/// assert_eq!(guard.duration(), Duration::from_secs(1)); +/// assert_eq!(progress.load(Ordering::Acquire), 1); +/// guard.allow_progress(); +/// +/// let guard = gate.expect_sleep().await; +/// assert_eq!(progress.load(Ordering::Acquire), 2); +/// assert_eq!(task.is_finished(), false); +/// guard.allow_progress(); +/// task.await.expect("successful completion"); +/// # }; +/// ``` +pub struct CapturedSleep<'a>(oneshot::Sender<()>, &'a SleepGate, Duration); +impl CapturedSleep<'_> { + /// Allow the calling code to advance past the call to [`AsyncSleep::sleep`] + /// + /// In order to facilitate testing with no flakiness, the future returned by the call to `sleep` + /// will not resolve until [`CapturedSleep`] is dropped or this method is called. + /// + /// ```rust + /// use std::time::Duration; + /// use aws_smithy_async::rt::sleep::AsyncSleep; + /// async fn do_something(sleep: &dyn AsyncSleep) { + /// println!("before sleep"); + /// sleep.sleep(Duration::from_secs(1)).await; + /// println!("after sleep"); + /// } + /// ``` + /// + /// To be specific, when `do_something` is called, the code will advance to `sleep.sleep`. + /// When [`SleepGate::expect_sleep`] is called, the 1 second sleep will be captured, but `after sleep` + /// WILL NOT be printed, until `allow_progress` is called. + pub fn allow_progress(self) { + drop(self) + } + + /// Duration in the call to [`AsyncSleep::sleep`] + pub fn duration(&self) -> Duration { + self.2 + } +} + +impl AsRef for CapturedSleep<'_> { + fn as_ref(&self) -> &Duration { + &self.2 + } +} + +impl SleepGate { + /// Expect the time source to sleep + /// + /// This returns the duration that was slept and a [`CapturedSleep`]. The drop guard is used + /// to precisely control + pub async fn expect_sleep(&mut self) -> CapturedSleep<'_> { + timeout(Duration::from_secs(1), self.gate.wait()) + .await + .expect("timeout"); + let dur = self + .pending + .lock() + .unwrap() + .take() + .unwrap_or(Duration::from_secs(123456)); + let guard = CapturedSleep( + self.advance_guard.lock().unwrap().take().unwrap(), + self, + dur, + ); + guard + } +} + +impl AsyncSleep for ControlledSleep { + fn sleep(&self, duration: Duration) -> Sleep { + let barrier = self.barrier.clone(); + let log = self.log.clone(); + let pending = self.duration.clone(); + let drop_guard = self.advance_guard.clone(); + Sleep::new(async move { + // 1. write the duration into the shared mutex + assert!(pending.lock().unwrap().is_none()); + *pending.lock().unwrap() = Some(duration); + let (tx, rx) = oneshot::channel(); + *drop_guard.lock().unwrap() = Some(tx); + // 2. first wait on the barrier—this is how we wait for an invocation of `expect_sleep` + barrier.wait().await; + log.lock().unwrap().push(duration); + let _ = dbg!(rx.await); + }) + } +} + +/// Returns a trio of tools to test interactions with time +/// +/// 1. [`ManualTimeSource`] which starts at a specific time and only advances when `sleep` is called. +/// It MUST be paired with [`ControlledSleep`] in order to function. +pub fn controlled_time_and_sleep( + start_time: SystemTime, +) -> (ManualTimeSource, ControlledSleep, SleepGate) { + let log = Arc::new(Mutex::new(vec![])); + let (sleep, gate) = ControlledSleep::new(log.clone()); + (ManualTimeSource { start_time, log }, sleep, gate) +} + +/// Returns a duo of tools to test interactions with time. Sleeps will end instantly, but the +/// desired length of the sleeps will be recorded for later verification. +pub fn instant_time_and_sleep(start_time: SystemTime) -> (ManualTimeSource, InstantSleep) { + let log = Arc::new(Mutex::new(vec![])); + let sleep = InstantSleep::new(log.clone()); + (ManualTimeSource { start_time, log }, sleep) +} + +impl TimeSource for SystemTime { + fn now(&self) -> SystemTime { + *self + } +} + +impl From for SharedTimeSource { + fn from(value: SystemTime) -> Self { + SharedTimeSource::new(value) + } +} + +impl From for SharedTimeSource { + fn from(value: ManualTimeSource) -> Self { + SharedTimeSource::new(value) + } +} + +#[cfg(test)] +mod test { + use crate::rt::sleep::AsyncSleep; + use crate::test_util::controlled_time_and_sleep; + use crate::time::TimeSource; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + use tokio::task::yield_now; + use tokio::time::timeout; + + #[tokio::test] + async fn test_sleep_gate() { + use std::time::{Duration, UNIX_EPOCH}; + let start = UNIX_EPOCH; + let (time, sleep, mut gate) = controlled_time_and_sleep(UNIX_EPOCH); + let progress = Arc::new(AtomicUsize::new(0)); + let task_progress = progress.clone(); + let task = tokio::spawn(async move { + assert_eq!(time.now(), start); + let progress = task_progress; + progress.store(1, Ordering::Release); + sleep.sleep(Duration::from_secs(1)).await; + assert_eq!(time.now(), start + Duration::from_secs(1)); + progress.store(2, Ordering::Release); + sleep.sleep(Duration::from_secs(2)).await; + assert_eq!(time.now(), start + Duration::from_secs(3)); + }); + while progress.load(Ordering::Acquire) != 1 { + yield_now().await + } + let guard = gate.expect_sleep().await; + assert_eq!(guard.duration(), Duration::from_secs(1)); + assert_eq!(progress.load(Ordering::Acquire), 1); + guard.allow_progress(); + + let guard = gate.expect_sleep().await; + assert_eq!(progress.load(Ordering::Acquire), 2); + assert!(!task.is_finished(), "task should not be finished"); + guard.allow_progress(); + timeout(Duration::from_secs(1), task) + .await + .expect("no timeout") + .expect("successful completion"); + } +} diff --git a/rust-runtime/aws-smithy-async/src/time.rs b/rust-runtime/aws-smithy-async/src/time.rs new file mode 100644 index 0000000000..b1b3f5ac1f --- /dev/null +++ b/rust-runtime/aws-smithy-async/src/time.rs @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Time source abstraction to support WASM and testing +use std::fmt::Debug; +use std::sync::Arc; +use std::time::SystemTime; + +/// Trait with a `now()` function returning the current time +pub trait TimeSource: Debug + Send + Sync { + /// Returns the current time + fn now(&self) -> SystemTime; +} + +/// Time source that delegates to [`SystemTime::now`] +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct SystemTimeSource; + +impl SystemTimeSource { + /// Creates a new SystemTimeSource + pub fn new() -> Self { + SystemTimeSource + } +} + +impl TimeSource for SystemTimeSource { + fn now(&self) -> SystemTime { + // this is the one OK usage + #[allow(clippy::disallowed_methods)] + SystemTime::now() + } +} + +impl Default for SharedTimeSource { + fn default() -> Self { + SharedTimeSource(Arc::new(SystemTimeSource)) + } +} + +/// Time source that always returns the same time +#[derive(Debug)] +pub struct StaticTimeSource { + time: SystemTime, +} + +impl StaticTimeSource { + /// Creates a new static time source that always returns the same time + pub fn new(time: SystemTime) -> Self { + Self { time } + } +} + +impl TimeSource for StaticTimeSource { + fn now(&self) -> SystemTime { + self.time + } +} + +impl From for SharedTimeSource { + fn from(value: StaticTimeSource) -> Self { + SharedTimeSource::new(value) + } +} + +#[derive(Debug, Clone)] +/// Time source structure used inside SDK +/// +/// This implements Default—the default implementation will use `SystemTime::now()` +pub struct SharedTimeSource(Arc); + +impl SharedTimeSource { + /// Returns the current time + pub fn now(&self) -> SystemTime { + self.0.now() + } + + /// Creates a new shared time source + pub fn new(source: impl TimeSource + 'static) -> Self { + Self(Arc::new(source)) + } +} + +impl TimeSource for SharedTimeSource { + fn now(&self) -> SystemTime { + self.0.now() + } +} diff --git a/rust-runtime/aws-smithy-client/Cargo.toml b/rust-runtime/aws-smithy-client/Cargo.toml index 43b40da126..5426587c9d 100644 --- a/rust-runtime/aws-smithy-client/Cargo.toml +++ b/rust-runtime/aws-smithy-client/Cargo.toml @@ -9,13 +9,14 @@ repository = "https://github.com/awslabs/smithy-rs" [features] rt-tokio = ["aws-smithy-async/rt-tokio"] -test-util = ["dep:aws-smithy-protocol-test", "dep:hyper", "hyper?/server", "hyper?/h2", "dep:serde", "serde?/derive", "rustls", "tokio/full"] -native-tls = ["dep:hyper-tls", "client-hyper", "rt-tokio"] +test-util = ["dep:aws-smithy-protocol-test", "dep:serde", "dep:serde_json", "serde?/derive"] +wiremock = ["test-util", "dep:hyper", "hyper?/server", "hyper?/h2", "rustls", "tokio/full"] +native-tls = [] +allow-compilation = [] # our tests use `cargo test --all-features` and native-tls breaks CI rustls = ["dep:hyper-rustls", "dep:lazy_static", "dep:rustls", "client-hyper", "rt-tokio"] -client-hyper = ["dep:hyper"] +client-hyper = ["dep:hyper", "hyper?/client", "hyper?/http2", "hyper?/http1", "hyper?/tcp"] hyper-webpki-doctest-only = ["dep:hyper-rustls", "hyper-rustls?/webpki-roots"] - [dependencies] aws-smithy-async = { path = "../aws-smithy-async" } aws-smithy-http = { path = "../aws-smithy-http" } @@ -23,25 +24,27 @@ aws-smithy-http-tower = { path = "../aws-smithy-http-tower" } aws-smithy-protocol-test = { path = "../aws-smithy-protocol-test", optional = true } aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" -fastrand = "1.4.0" +fastrand = "2.0.0" http = "0.2.3" http-body = "0.4.4" -hyper = { version = "0.14.25", features = ["client", "http2", "http1", "tcp"], optional = true } -# cargo does not support optional test dependencies, so to completely disable rustls when -# the native-tls feature is enabled, we need to add the webpki-roots feature here. +hyper = { version = "0.14.26", default-features = false, optional = true } +# cargo does not support optional test dependencies, so to completely disable rustls +# we need to add the webpki-roots feature here. # https://github.com/rust-lang/cargo/issues/1596 -hyper-rustls = { version = "0.23.0", optional = true, features = ["rustls-native-certs", "http2"] } +hyper-rustls = { version = "0.24", optional = true, features = ["rustls-native-certs", "http2"] } hyper-tls = { version = "0.5.0", optional = true } -rustls = { version = "0.20", optional = true } +rustls = { version = "0.21.1", optional = true } lazy_static = { version = "1", optional = true } pin-project-lite = "0.2.7" serde = { version = "1", features = ["derive"], optional = true } +serde_json = { version = "1", optional = true } tokio = { version = "1.13.1" } tower = { version = "0.4.6", features = ["util", "retry"] } tracing = "0.1" [dev-dependencies] aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio"] } +hyper-tls = { version = "0.5.0" } serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1.23.1", features = ["full", "test-util"] } diff --git a/rust-runtime/aws-smithy-client/additional-ci b/rust-runtime/aws-smithy-client/additional-ci index fde542e9fc..e2ada6a73e 100755 --- a/rust-runtime/aws-smithy-client/additional-ci +++ b/rust-runtime/aws-smithy-client/additional-ci @@ -8,8 +8,12 @@ set -e +# TODO(msrvUpgrade): This can be enabled when upgrading to Rust 1.71 +# echo '### Checking compilation under WASM' +# cargo hack check --no-dev-deps --target wasm32-unknown-unknown + echo "### Checking for duplicate dependency versions in the normal dependency graph with all features enabled" cargo tree -d --edges normal --all-features echo "### Testing every combination of features (excluding --all-features)" -cargo hack test --feature-powerset --exclude-all-features +cargo hack test --feature-powerset --exclude-all-features --exclude-features native-tls diff --git a/rust-runtime/aws-smithy-client/external-types.toml b/rust-runtime/aws-smithy-client/external-types.toml index 0bab3e6536..74daaad215 100644 --- a/rust-runtime/aws-smithy-client/external-types.toml +++ b/rust-runtime/aws-smithy-client/external-types.toml @@ -3,6 +3,7 @@ allowed_external_types = [ "aws_smithy_http::*", "aws_smithy_http_tower::*", "aws_smithy_types::*", + "aws_smithy_protocol_test::MediaType", "http::header::name::HeaderName", "http::request::Request", "http::response::Response", @@ -10,7 +11,7 @@ allowed_external_types = [ "tower::retry::policy::Policy", "tower_service::Service", - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Move `rustls`/`native-tls` features into separate crates + # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Move `rustls` feature into separate crates "hyper::client::connect::http::HttpConnector", "hyper_rustls::connector::HttpsConnector", "hyper_tls::client::HttpsConnector", @@ -22,7 +23,7 @@ allowed_external_types = [ "tokio::io::async_write::AsyncWrite", - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Once tooling permits it, only allow the following types in the `test-utils` feature + # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Once tooling permits it, only allow the following types in the `test-util` feature "bytes::bytes::Bytes", "serde::ser::Serialize", "serde::de::Deserialize", diff --git a/rust-runtime/aws-smithy-client/src/builder.rs b/rust-runtime/aws-smithy-client/src/builder.rs index d226dcb1cf..5602c330f5 100644 --- a/rust-runtime/aws-smithy-client/src/builder.rs +++ b/rust-runtime/aws-smithy-client/src/builder.rs @@ -4,12 +4,11 @@ */ use crate::{bounds, erase, retry, Client}; -use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; +use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_types::retry::ReconnectMode; use aws_smithy_types::timeout::{OperationTimeoutConfig, TimeoutConfig}; -use std::sync::Arc; #[derive(Clone, Debug)] struct MaybeRequiresSleep { @@ -37,7 +36,7 @@ pub struct Builder { middleware: M, retry_policy: MaybeRequiresSleep, operation_timeout_config: Option, - sleep_impl: Option>, + sleep_impl: Option, reconnect_mode: Option, } @@ -88,21 +87,24 @@ where } } -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] use crate::erase::DynConnector; -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] use crate::http_connector::ConnectorSettings; -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] use crate::hyper_ext::Adapter as HyperAdapter; +#[cfg(all(feature = "native-tls", not(feature = "allow-compilation")))] +compile_error!("Feature native-tls has been removed. For upgrade instructions, see: https://awslabs.github.io/smithy-rs/design/transport/connector.html"); + /// Max idle connections is not standardized across SDKs. Java V1 and V2 use 50, and Go V2 uses 100. /// The number below was chosen arbitrarily between those two reference points, and should allow /// for 14 separate SDK clients in a Lambda where the max file handles is 1024. -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] const DEFAULT_MAX_IDLE_CONNECTIONS: usize = 70; /// Returns default HTTP client settings for hyper. -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] fn default_hyper_builder() -> hyper::client::Builder { let mut builder = hyper::client::Builder::default(); builder.pool_max_idle_per_host(DEFAULT_MAX_IDLE_CONNECTIONS); @@ -125,24 +127,7 @@ impl Builder<(), M, R> { } } -#[cfg(feature = "native-tls")] -impl Builder<(), M, R> { - /// Connect to the service over HTTPS using the native TLS library on your - /// platform using dynamic dispatch. - pub fn native_tls_connector( - self, - connector_settings: ConnectorSettings, - ) -> Builder { - self.connector(DynConnector::new( - HyperAdapter::builder() - .hyper_builder(default_hyper_builder()) - .connector_settings(connector_settings) - .build(crate::conns::native_tls()), - )) - } -} - -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] impl Builder<(), M, R> { /// Create a Smithy client builder with an HTTPS connector and the [standard retry /// policy](crate::retry::Standard) over the default middleware implementation. @@ -150,17 +135,13 @@ impl Builder<(), M, R> { /// For convenience, this constructor type-erases the concrete TLS connector backend used using /// dynamic dispatch. This comes at a slight runtime performance cost. See /// [`DynConnector`](crate::erase::DynConnector) for details. To avoid that overhead, use - /// [`Builder::rustls_connector`] or [`Builder::native_tls_connector`] instead. + /// [`Builder::rustls_connector`] instead. + #[cfg(feature = "rustls")] pub fn dyn_https_connector( self, connector_settings: ConnectorSettings, ) -> Builder { - #[cfg(feature = "rustls")] let with_https = |b: Builder<_, M, R>| b.rustls_connector(connector_settings); - // If we are compiling this function & rustls is not enabled, then native-tls MUST be enabled - #[cfg(not(feature = "rustls"))] - let with_https = |b: Builder<_, M, R>| b.native_tls_connector(connector_settings); - with_https(self) } } @@ -330,14 +311,14 @@ impl Builder { self } - /// Set the [`AsyncSleep`] function that the [`Client`] will use to create things like timeout futures. - pub fn set_sleep_impl(&mut self, async_sleep: Option>) -> &mut Self { + /// Set [`aws_smithy_async::rt::sleep::SharedAsyncSleep`] that the [`Client`] will use to create things like timeout futures. + pub fn set_sleep_impl(&mut self, async_sleep: Option) -> &mut Self { self.sleep_impl = async_sleep; self } - /// Set the [`AsyncSleep`] function that the [`Client`] will use to create things like timeout futures. - pub fn sleep_impl(mut self, async_sleep: Arc) -> Self { + /// Set [`aws_smithy_async::rt::sleep::SharedAsyncSleep`] that the [`Client`] will use to create things like timeout futures. + pub fn sleep_impl(mut self, async_sleep: SharedAsyncSleep) -> Self { self.set_sleep_impl(Some(async_sleep)); self } @@ -476,7 +457,7 @@ where mod tests { use super::*; use crate::never::NeverConnector; - use aws_smithy_async::rt::sleep::Sleep; + use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; use std::panic::{self, AssertUnwindSafe}; use std::time::Duration; @@ -582,8 +563,5 @@ mod tests { #[cfg(feature = "rustls")] let _builder: Builder = Builder::new().rustls_connector(Default::default()); - #[cfg(feature = "native-tls")] - let _builder: Builder = - Builder::new().native_tls_connector(Default::default()); } } diff --git a/rust-runtime/aws-smithy-client/src/conns.rs b/rust-runtime/aws-smithy-client/src/conns.rs index 6ccf07f462..8ee1d8a929 100644 --- a/rust-runtime/aws-smithy-client/src/conns.rs +++ b/rust-runtime/aws-smithy-client/src/conns.rs @@ -5,16 +5,13 @@ //! Type aliases for standard connection types. +use crate::erase::DynConnector; + #[cfg(feature = "rustls")] /// A `hyper` connector that uses the `rustls` crate for TLS. To use this in a smithy client, /// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter). pub type Https = hyper_rustls::HttpsConnector; -#[cfg(feature = "native-tls")] -/// A `hyper` connector that uses the `native-tls` crate for TLS. To use this in a smithy client, -/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter). -pub type NativeTls = hyper_tls::HttpsConnector; - #[cfg(feature = "rustls")] /// A smithy connector that uses the `rustls` crate for TLS. pub type Rustls = crate::hyper_ext::Adapter; @@ -54,6 +51,63 @@ lazy_static::lazy_static! { }; } +mod default_connector { + use crate::erase::DynConnector; + use crate::http_connector::ConnectorSettings; + use aws_smithy_async::rt::sleep::SharedAsyncSleep; + + #[cfg(feature = "rustls")] + fn base( + settings: &ConnectorSettings, + sleep: Option, + ) -> crate::hyper_ext::Builder { + let mut hyper = crate::hyper_ext::Adapter::builder().connector_settings(settings.clone()); + if let Some(sleep) = sleep { + hyper = hyper.sleep_impl(sleep); + } + hyper + } + + /// Given `ConnectorSettings` and an `SharedAsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. + pub fn default_connector( + settings: &ConnectorSettings, + sleep: Option, + ) -> Option { + #[cfg(feature = "rustls")] + { + tracing::trace!(settings = ?settings, sleep = ?sleep, "creating a new default connector"); + let hyper = base(settings, sleep).build(super::https()); + Some(DynConnector::new(hyper)) + } + #[cfg(not(feature = "rustls"))] + { + tracing::trace!(settings = ?settings, sleep = ?sleep, "no default connector available"); + None + } + } +} +pub use default_connector::default_connector; + +/// Error that indicates a connector is required. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct ConnectorRequiredError; + +impl std::fmt::Display for ConnectorRequiredError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this.") + } +} + +impl std::error::Error for ConnectorRequiredError {} + +/// Converts an optional connector to a result. +pub fn require_connector( + connector: Option, +) -> Result { + connector.ok_or(ConnectorRequiredError) +} + #[cfg(feature = "rustls")] /// Return a default HTTPS connector backed by the `rustls` crate. /// @@ -63,25 +117,7 @@ pub fn https() -> Https { HTTPS_NATIVE_ROOTS.clone() } -#[cfg(feature = "native-tls")] -/// Return a default HTTPS connector backed by the `hyper_tls` crate. -/// -/// It requires a minimum TLS version of 1.2. -/// It allows you to connect to both `http` and `https` URLs. -pub fn native_tls() -> NativeTls { - // `TlsConnector` actually comes for here: https://docs.rs/native-tls/latest/native_tls/ - // hyper_tls just re-exports the crate for convenience. - let mut tls = hyper_tls::native_tls::TlsConnector::builder(); - let tls = tls - .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12)) - .build() - .unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e)); - let mut http = hyper::client::HttpConnector::new(); - http.enforce_http(false); - hyper_tls::HttpsConnector::from((http, tls.into())) -} - -#[cfg(all(test, any(feature = "native-tls", feature = "rustls")))] +#[cfg(all(test, feature = "rustls"))] mod tests { use crate::erase::DynConnector; use crate::hyper_ext::Adapter; @@ -100,30 +136,6 @@ mod tests { assert!(res.status().is_success()); } - #[cfg(feature = "native-tls")] - mod native_tls_tests { - use super::super::native_tls; - use super::*; - - #[tokio::test] - async fn test_native_tls_connector_can_make_http_requests() { - let conn = Adapter::builder().build(native_tls()); - let conn = DynConnector::new(conn); - let http_uri: Uri = "http://example.com/".parse().unwrap(); - - send_request_and_assert_success(conn, &http_uri).await; - } - - #[tokio::test] - async fn test_native_tls_connector_can_make_https_requests() { - let conn = Adapter::builder().build(native_tls()); - let conn = DynConnector::new(conn); - let https_uri: Uri = "https://example.com/".parse().unwrap(); - - send_request_and_assert_success(conn, &https_uri).await; - } - } - #[cfg(feature = "rustls")] mod rustls_tests { use super::super::https; @@ -148,3 +160,61 @@ mod tests { } } } + +#[cfg(test)] +mod custom_tls_tests { + use crate::erase::DynConnector; + use crate::hyper_ext::Adapter; + use aws_smithy_http::body::SdkBody; + use http::{Method, Request, Uri}; + use tower::{Service, ServiceBuilder}; + + type NativeTls = hyper_tls::HttpsConnector; + + fn native_tls() -> NativeTls { + let mut tls = hyper_tls::native_tls::TlsConnector::builder(); + let tls = tls + .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12)) + .build() + .unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e)); + let mut http = hyper::client::HttpConnector::new(); + http.enforce_http(false); + hyper_tls::HttpsConnector::from((http, tls.into())) + } + + #[tokio::test] + async fn test_native_tls_connector_can_make_http_requests() { + let conn = Adapter::builder().build(native_tls()); + let conn = DynConnector::new(conn); + let http_uri: Uri = "http://example.com/".parse().unwrap(); + + send_request_and_assert_success(conn, &http_uri).await; + } + + #[tokio::test] + async fn test_native_tls_connector_can_make_https_requests() { + let conn = Adapter::builder().build(native_tls()); + let conn = DynConnector::new(conn); + let https_uri: Uri = "https://example.com/".parse().unwrap(); + + send_request_and_assert_success(conn, &https_uri).await; + } + + async fn send_request_and_assert_success(conn: DynConnector, uri: &Uri) { + let mut svc = ServiceBuilder::new().service(conn); + let mut att = 0; + let res = loop { + let req = Request::builder() + .uri(uri) + .method(Method::GET) + .body(SdkBody::empty()) + .unwrap(); + if let Ok(res) = svc.call(req).await { + break res; + } + assert!(att < 5); + att += 1; + }; + assert!(res.status().is_success()); + } +} diff --git a/rust-runtime/aws-smithy-client/src/dvr.rs b/rust-runtime/aws-smithy-client/src/dvr.rs index be48521994..f0f68a4709 100644 --- a/rust-runtime/aws-smithy-client/src/dvr.rs +++ b/rust-runtime/aws-smithy-client/src/dvr.rs @@ -14,6 +14,7 @@ use std::collections::HashMap; use bytes::Bytes; use serde::{Deserialize, Serialize}; +pub use aws_smithy_protocol_test::MediaType; use aws_smithy_types::base64; pub use record::RecordingConnection; pub use replay::ReplayingConnection; diff --git a/rust-runtime/aws-smithy-client/src/dvr/record.rs b/rust-runtime/aws-smithy-client/src/dvr/record.rs index 7d1f95a290..9415528f73 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/record.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/record.rs @@ -19,6 +19,8 @@ use crate::dvr::{self, Action, BodyData, ConnectionId, Direction, Error, Network use super::Event; use std::fmt::Display; +use std::io; +use std::path::Path; /// Recording Connection Wrapper /// @@ -30,13 +32,13 @@ pub struct RecordingConnection { pub(crate) inner: S, } -impl RecordingConnection { +#[cfg(all(feature = "rustls", feature = "client-hyper"))] +impl RecordingConnection> { /// Construct a recording connection wrapping a default HTTPS implementation - #[cfg(feature = "hyper-rustls")] pub fn https() -> Self { Self { data: Default::default(), - inner: crate::conns::https(), + inner: crate::hyper_ext::Adapter::builder().build(crate::conns::https()), num_events: Arc::new(AtomicUsize::new(0)), } } @@ -66,6 +68,14 @@ impl RecordingConnection { } } + /// Dump the network traffic to a file + pub fn dump_to_file(&self, path: impl AsRef) -> Result<(), io::Error> { + std::fs::write( + path, + serde_json::to_string(&self.network_traffic()).unwrap(), + ) + } + fn next_id(&self) -> ConnectionId { ConnectionId(self.num_events.fetch_add(1, Ordering::Relaxed)) } diff --git a/rust-runtime/aws-smithy-client/src/dvr/replay.rs b/rust-runtime/aws-smithy-client/src/dvr/replay.rs index 95bb78468d..27e606f082 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/replay.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/replay.rs @@ -6,6 +6,7 @@ use std::collections::{HashMap, VecDeque}; use std::error::Error; use std::ops::DerefMut; +use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; @@ -17,8 +18,10 @@ use tokio::task::JoinHandle; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; +use aws_smithy_protocol_test::MediaType; +use aws_smithy_types::error::display::DisplayErrorContext; -use crate::dvr::{Action, ConnectionId, Direction, Event}; +use crate::dvr::{Action, ConnectionId, Direction, Event, NetworkTraffic}; /// Wrapper type to enable optionally waiting for a future to complete #[derive(Debug)] @@ -59,11 +62,44 @@ impl ReplayingConnection { ConnectionId(self.num_events.fetch_add(1, Ordering::Relaxed)) } + /// Validate all headers and bodies + pub async fn full_validate(self, media_type: MediaType) -> Result<(), Box> { + self.validate_body_and_headers(None, media_type).await + } + /// Validate actual requests against expected requests pub async fn validate( self, checked_headers: &[&str], body_comparer: impl Fn(&[u8], &[u8]) -> Result<(), Box>, + ) -> Result<(), Box> { + self.validate_base(Some(checked_headers), body_comparer) + .await + } + + /// Validate that the bodies match, using a given [`MediaType`] for comparison + /// + /// The specified headers are also validated + pub async fn validate_body_and_headers( + self, + checked_headers: Option<&[&str]>, + media_type: MediaType, + ) -> Result<(), Box> { + self.validate_base(checked_headers, |b1, b2| { + aws_smithy_protocol_test::validate_body( + b1, + std::str::from_utf8(b2).unwrap(), + media_type.clone(), + ) + .map_err(|e| Box::new(e) as _) + }) + .await + } + + async fn validate_base( + self, + checked_headers: Option<&[&str]>, + body_comparer: impl Fn(&[u8], &[u8]) -> Result<(), Box>, ) -> Result<(), Box> { let mut actual_requests = std::mem::take(self.recorded_requests.lock().unwrap().deref_mut()); @@ -78,17 +114,23 @@ impl ReplayingConnection { ))? .take() .await; - aws_smithy_protocol_test::assert_uris_match(actual.uri(), expected.uri()); + aws_smithy_protocol_test::assert_uris_match(expected.uri(), actual.uri()); body_comparer(expected.body().as_ref(), actual.body().as_ref())?; - let expected_headers = checked_headers - .iter() + let expected_headers = expected + .headers() + .keys() + .map(|k| k.as_str()) + .filter(|k| match checked_headers { + Some(list) => list.contains(k), + None => true, + }) .flat_map(|key| { - let _ = expected.headers().get(*key)?; + let _ = expected.headers().get(key)?; Some(( - *key, + key, expected .headers() - .get_all(*key) + .get_all(key) .iter() .map(|h| h.to_str().unwrap()) .collect::>() @@ -96,7 +138,14 @@ impl ReplayingConnection { )) }) .collect::>(); - aws_smithy_protocol_test::validate_headers(actual.headers(), expected_headers)?; + aws_smithy_protocol_test::validate_headers(actual.headers(), expected_headers) + .map_err(|err| { + format!( + "event {} validation failed with: {}", + conn_id.0, + DisplayErrorContext(&err) + ) + })?; } Ok(()) } @@ -118,6 +167,13 @@ impl ReplayingConnection { out } + /// Build a replay connection from a JSON file + pub fn from_file(path: impl AsRef) -> Result> { + let events: NetworkTraffic = + serde_json::from_str(&std::fs::read_to_string(path.as_ref())?)?; + Ok(Self::new(events.events)) + } + /// Build a replay connection from a sequence of events pub fn new(events: Vec) -> Self { let mut event_map: HashMap<_, VecDeque<_>> = HashMap::new(); @@ -224,6 +280,7 @@ impl tower::Service> for ReplayingConnection { fn call(&mut self, mut req: Request) -> Self::Future { let event_id = self.next_id(); + tracing::debug!("received event {}: {req:?}", event_id.0); let mut events = match self.live_events.lock().unwrap().remove(&event_id) { Some(traffic) => traffic, None => { diff --git a/rust-runtime/aws-smithy-client/src/erase.rs b/rust-runtime/aws-smithy-client/src/erase.rs index 648562192c..d966664b3b 100644 --- a/rust-runtime/aws-smithy-client/src/erase.rs +++ b/rust-runtime/aws-smithy-client/src/erase.rs @@ -164,6 +164,16 @@ impl DynConnector { { Self(BoxCloneService::new(connector.map_err(|e| e.into()))) } + + #[doc(hidden)] + pub fn call_lite( + &mut self, + req: http::Request, + ) -> BoxFuture, Box> + { + let future = Service::call(self, req); + Box::pin(async move { future.await.map_err(|err| Box::new(err) as _) }) + } } impl Service> for DynConnector { diff --git a/rust-runtime/aws-smithy-client/src/http_connector.rs b/rust-runtime/aws-smithy-client/src/http_connector.rs index 67db6e6918..eaac5805c6 100644 --- a/rust-runtime/aws-smithy-client/src/http_connector.rs +++ b/rust-runtime/aws-smithy-client/src/http_connector.rs @@ -7,14 +7,15 @@ //! that enable passing HTTP connectors around. use crate::erase::DynConnector; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use aws_smithy_types::timeout::TimeoutConfig; use std::time::Duration; use std::{fmt::Debug, sync::Arc}; /// Type alias for a Connector factory function. pub type MakeConnectorFn = - dyn Fn(&ConnectorSettings, Option>) -> Option + Send + Sync; + dyn Fn(&ConnectorSettings, Option) -> Option + Send + Sync; /// Enum for describing the two "kinds" of HTTP Connectors in smithy-rs. #[derive(Clone)] @@ -41,13 +42,17 @@ impl Debug for HttpConnector { } } +impl Storable for HttpConnector { + type Storer = StoreReplace; +} + impl HttpConnector { /// If `HttpConnector` is `Prebuilt`, return a clone of that connector. /// If `HttpConnector` is `ConnectorFn`, generate a new connector from settings and return it. pub fn connector( &self, settings: &ConnectorSettings, - sleep: Option>, + sleep: Option, ) -> Option { match self { HttpConnector::Prebuilt(conn) => conn.clone(), diff --git a/rust-runtime/aws-smithy-client/src/hyper_ext.rs b/rust-runtime/aws-smithy-client/src/hyper_ext.rs index 7032604a78..be1e5679d2 100644 --- a/rust-runtime/aws-smithy-client/src/hyper_ext.rs +++ b/rust-runtime/aws-smithy-client/src/hyper_ext.rs @@ -18,19 +18,10 @@ //! [`Client`](crate::Client), directly, use the `dyn_https_https()` method to match that default behavior: //! #![cfg_attr( - not(all( - any(feature = "rustls", feature = "native-tls"), - feature = "client-hyper" - )), + not(all(feature = "rustls", feature = "client-hyper")), doc = "```no_run,ignore" )] -#![cfg_attr( - all( - any(feature = "rustls", feature = "native-tls"), - feature = "client-hyper" - ), - doc = "```no_run" -)] +#![cfg_attr(all(feature = "rustls", feature = "client-hyper"), doc = "```no_run")] //! use aws_smithy_client::Client; //! //! let client = Client::builder() @@ -90,7 +81,7 @@ use crate::http_connector::ConnectorSettings; use crate::hyper_ext::timeout_middleware::{ConnectTimeout, HttpReadTimeout, HttpTimeoutError}; use crate::never::stream::EmptyStream; use aws_smithy_async::future::timeout::TimedOutError; -use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; +use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; @@ -104,8 +95,6 @@ use hyper::client::connect::{ use std::error::Error; use std::fmt::Debug; -use std::sync::Arc; - use crate::erase::boxclone::BoxFuture; use aws_smithy_http::connection::{CaptureSmithyConnection, ConnectionMetadata}; use tokio::io::{AsyncRead, AsyncWrite}; @@ -237,8 +226,8 @@ fn find_source<'a, E: Error + 'static>(err: &'a (dyn Error + 'static)) -> Option /// Builder for [`hyper_ext::Adapter`](Adapter) /// /// Unlike a Smithy client, the [`Service`] inside a [`hyper_ext::Adapter`](Adapter) is actually a service that -/// accepts a `Uri` and returns a TCP stream. Two default implementations of this are provided, one -/// that encrypts the stream with `rustls`, the other that encrypts the stream with `native-tls`. +/// accepts a `Uri` and returns a TCP stream. One default implementation of this is provided, +/// that encrypts the stream with `rustls`. /// /// # Examples /// Construct a HyperAdapter with the default HTTP implementation (rustls). This can be useful when you want to share a Hyper connector @@ -261,7 +250,7 @@ fn find_source<'a, E: Error + 'static>(err: &'a (dyn Error + 'static)) -> Option #[derive(Default, Debug)] pub struct Builder { connector_settings: Option, - sleep_impl: Option>, + sleep_impl: Option, client_builder: Option, } @@ -297,9 +286,7 @@ impl Builder { let read_timeout = match read_timeout { Some(duration) => HttpReadTimeout::new( base, - sleep_impl - .clone() - .expect("a sleep impl must be provided in order to have a read timeout"), + sleep_impl.expect("a sleep impl must be provided in order to have a read timeout"), duration, ), None => HttpReadTimeout::no_timeout(base), @@ -313,7 +300,7 @@ impl Builder { /// /// Calling this is only necessary for testing or to use something other than /// [`default_async_sleep`]. - pub fn sleep_impl(mut self, sleep_impl: Arc) -> Self { + pub fn sleep_impl(mut self, sleep_impl: SharedAsyncSleep) -> Self { self.sleep_impl = Some(sleep_impl); self } @@ -322,10 +309,7 @@ impl Builder { /// /// Calling this is only necessary for testing or to use something other than /// [`default_async_sleep`]. - pub fn set_sleep_impl( - &mut self, - sleep_impl: Option>, - ) -> &mut Self { + pub fn set_sleep_impl(&mut self, sleep_impl: Option) -> &mut Self { self.sleep_impl = sleep_impl; self } @@ -370,7 +354,6 @@ mod timeout_middleware { use std::fmt::Formatter; use std::future::Future; use std::pin::Pin; - use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; @@ -379,8 +362,8 @@ mod timeout_middleware { use tower::BoxError; use aws_smithy_async::future::timeout::{TimedOutError, Timeout}; - use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_async::rt::sleep::Sleep; + use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; #[derive(Debug)] pub(crate) struct HttpTimeoutError { @@ -414,14 +397,14 @@ mod timeout_middleware { #[derive(Clone, Debug)] pub(super) struct ConnectTimeout { inner: I, - timeout: Option<(Arc, Duration)>, + timeout: Option<(SharedAsyncSleep, Duration)>, } impl ConnectTimeout { /// Create a new `ConnectTimeout` around `inner`. /// /// Typically, `I` will implement [`hyper::client::connect::Connect`]. - pub(crate) fn new(inner: I, sleep: Arc, timeout: Duration) -> Self { + pub(crate) fn new(inner: I, sleep: SharedAsyncSleep, timeout: Duration) -> Self { Self { inner, timeout: Some((sleep, timeout)), @@ -439,14 +422,14 @@ mod timeout_middleware { #[derive(Clone, Debug)] pub(crate) struct HttpReadTimeout { inner: I, - timeout: Option<(Arc, Duration)>, + timeout: Option<(SharedAsyncSleep, Duration)>, } impl HttpReadTimeout { /// Create a new `HttpReadTimeout` around `inner`. /// /// Typically, `I` will implement [`tower::Service>`]. - pub(crate) fn new(inner: I, sleep: Arc, timeout: Duration) -> Self { + pub(crate) fn new(inner: I, sleep: SharedAsyncSleep, timeout: Duration) -> Self { Self { inner, timeout: Some((sleep, timeout)), @@ -574,11 +557,10 @@ mod timeout_middleware { use crate::hyper_ext::Adapter; use crate::never::{NeverConnected, NeverReplies}; use aws_smithy_async::assert_elapsed; - use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::timeout::TimeoutConfig; - use std::sync::Arc; use std::time::Duration; use tower::Service; @@ -600,7 +582,7 @@ mod timeout_middleware { ); let mut hyper = Adapter::builder() .connector_settings(connector_settings) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(inner); let now = tokio::time::Instant::now(); tokio::time::pause(); @@ -639,7 +621,7 @@ mod timeout_middleware { ); let mut hyper = Adapter::builder() .connector_settings(connector_settings) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(inner); let now = tokio::time::Instant::now(); tokio::time::pause(); diff --git a/rust-runtime/aws-smithy-client/src/lib.rs b/rust-runtime/aws-smithy-client/src/lib.rs index 080e5ac9eb..82ddc4cacb 100644 --- a/rust-runtime/aws-smithy-client/src/lib.rs +++ b/rust-runtime/aws-smithy-client/src/lib.rs @@ -10,7 +10,6 @@ //! | `event-stream` | Provides Sender/Receiver implementations for Event Stream codegen. | //! | `rt-tokio` | Run async code with the `tokio` runtime | //! | `test-util` | Include various testing utils | -//! | `native-tls` | Use `native-tls` as the HTTP client's TLS implementation | //! | `rustls` | Use `rustls` as the HTTP client's TLS implementation | //! | `client-hyper` | Use `hyper` to handle HTTP requests | @@ -35,7 +34,7 @@ pub mod timeout; mod builder; pub use builder::Builder; -#[cfg(feature = "test-util")] +#[cfg(all(feature = "test-util", feature = "client-hyper"))] pub mod dvr; #[cfg(feature = "test-util")] pub mod test_connection; @@ -52,7 +51,7 @@ pub mod hyper_ext; pub mod static_tests; use crate::poison::PoisonLayer; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_http::operation::Operation; use aws_smithy_http::response::ParseHttpResponse; @@ -63,7 +62,6 @@ use aws_smithy_http_tower::parse_response::ParseResponseLayer; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::retry::{ProvideErrorKind, ReconnectMode}; use aws_smithy_types::timeout::OperationTimeoutConfig; -use std::sync::Arc; use timeout::ClientTimeoutParams; pub use timeout::TimeoutLayer; use tower::{Service, ServiceBuilder, ServiceExt}; @@ -85,9 +83,8 @@ use tracing::{debug_span, field, Instrument}; /// to the inner service, and then ultimately returning the inner service's response. /// /// With the `hyper` feature enabled, you can construct a `Client` directly from a -/// `hyper::Client` using `hyper_ext::Adapter::builder`. You can also enable the `rustls` or `native-tls` -/// features to construct a Client against a standard HTTPS endpoint using `Builder::rustls_connector` and -/// `Builder::native_tls_connector` respectively. +/// `hyper::Client` using `hyper_ext::Adapter::builder`. You can also enable the `rustls` +/// feature to construct a Client against a standard HTTPS endpoint using `Builder::rustls_connector`. #[derive(Debug)] pub struct Client< Connector = erase::DynConnector, @@ -99,7 +96,7 @@ pub struct Client< retry_policy: RetryPolicy, reconnect_mode: ReconnectMode, operation_timeout_config: OperationTimeoutConfig, - sleep_impl: Option>, + sleep_impl: Option, } impl Client<(), (), ()> { @@ -259,7 +256,7 @@ where > + Clone, { let _ = |o: static_tests::ValidTestOperation| { - let _ = self.call_raw(o); + drop(self.call_raw(o)); }; } } diff --git a/rust-runtime/aws-smithy-client/src/retry.rs b/rust-runtime/aws-smithy-client/src/retry.rs index 2c38807fae..8c2fdc246c 100644 --- a/rust-runtime/aws-smithy-client/src/retry.rs +++ b/rust-runtime/aws-smithy-client/src/retry.rs @@ -19,7 +19,7 @@ use std::time::Duration; use tracing::Instrument; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_http::operation::Operation; use aws_smithy_http::retry::ClassifyRetry; @@ -40,7 +40,7 @@ where type Policy; /// Create a new policy mechanism instance. - fn new_request_policy(&self, sleep_impl: Option>) -> Self::Policy; + fn new_request_policy(&self, sleep_impl: Option) -> Self::Policy; } /// Retry Policy Configuration @@ -165,7 +165,7 @@ impl Standard { impl NewRequestPolicy for Standard { type Policy = RetryHandler; - fn new_request_policy(&self, sleep_impl: Option>) -> Self::Policy { + fn new_request_policy(&self, sleep_impl: Option) -> Self::Policy { RetryHandler { local: RequestLocalRetryState::new(), shared: self.shared_state.clone(), @@ -262,7 +262,7 @@ pub struct RetryHandler { local: RequestLocalRetryState, shared: CrossRequestRetryState, config: Config, - sleep_impl: Option>, + sleep_impl: Option, } #[cfg(test)] diff --git a/rust-runtime/aws-smithy-client/src/test_connection.rs b/rust-runtime/aws-smithy-client/src/test_connection.rs index 86466944df..a339eac6bb 100644 --- a/rust-runtime/aws-smithy-client/src/test_connection.rs +++ b/rust-runtime/aws-smithy-client/src/test_connection.rs @@ -358,6 +358,7 @@ impl tower::Service> for ConnectionFn { /// match_events!(ev!(dns), ev!(connect), ev!(http(200)))(&mock.events()); /// # } /// ``` +#[cfg(feature = "wiremock")] pub mod wire_mock { use bytes::Bytes; use http::{Request, Response}; @@ -673,7 +674,7 @@ pub mod wire_mock { #[cfg(test)] mod tests { - use hyper::service::Service; + use tower::Service; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; diff --git a/rust-runtime/aws-smithy-client/src/timeout.rs b/rust-runtime/aws-smithy-client/src/timeout.rs index 640e1c4562..f9a03a41f6 100644 --- a/rust-runtime/aws-smithy-client/src/timeout.rs +++ b/rust-runtime/aws-smithy-client/src/timeout.rs @@ -7,13 +7,12 @@ use crate::SdkError; use aws_smithy_async::future::timeout::Timeout; -use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_http::operation::Operation; use aws_smithy_types::timeout::OperationTimeoutConfig; use pin_project_lite::pin_project; use std::future::Future; use std::pin::Pin; -use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; use tower::Layer; @@ -46,7 +45,7 @@ pub struct TimeoutServiceParams { /// The kind of timeouts created from these params kind: &'static str, /// The AsyncSleep impl that will be used to create time-limited futures - async_sleep: Arc, + async_sleep: SharedAsyncSleep, } #[derive(Clone, Debug, Default)] @@ -61,7 +60,7 @@ pub(crate) struct ClientTimeoutParams { impl ClientTimeoutParams { pub(crate) fn new( timeout_config: &OperationTimeoutConfig, - async_sleep: Option>, + async_sleep: Option, ) -> Self { if let Some(async_sleep) = async_sleep { Self { @@ -232,11 +231,10 @@ mod test { use crate::never::NeverService; use crate::{SdkError, TimeoutLayer}; use aws_smithy_async::assert_elapsed; - use aws_smithy_async::rt::sleep::{AsyncSleep, TokioSleep}; + use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::operation::{Operation, Request}; use aws_smithy_types::timeout::TimeoutConfig; - use std::sync::Arc; use std::time::Duration; use tower::{Service, ServiceBuilder, ServiceExt}; @@ -250,7 +248,7 @@ mod test { .operation_timeout(Duration::from_secs_f32(0.25)) .build(), ); - let sleep_impl: Arc = Arc::new(TokioSleep::new()); + let sleep_impl = SharedAsyncSleep::new(TokioSleep::new()); let timeout_service_params = ClientTimeoutParams::new(&timeout_config, Some(sleep_impl)); let mut svc = ServiceBuilder::new() .layer(TimeoutLayer::new(timeout_service_params.operation_timeout)) diff --git a/rust-runtime/aws-smithy-client/tests/e2e_test.rs b/rust-runtime/aws-smithy-client/tests/e2e_test.rs index 0a8594d6b3..f18a084bb5 100644 --- a/rust-runtime/aws-smithy-client/tests/e2e_test.rs +++ b/rust-runtime/aws-smithy-client/tests/e2e_test.rs @@ -5,14 +5,13 @@ mod test_operation; use crate::test_operation::{TestOperationParser, TestRetryClassifier}; -use aws_smithy_async::rt::sleep::TokioSleep; +use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_client::Client; use aws_smithy_http::body::SdkBody; use aws_smithy_http::operation; use aws_smithy_http::operation::Operation; use aws_smithy_http::result::SdkError; -use std::sync::Arc; use std::time::Duration; use tower::layer::util::Identity; @@ -72,7 +71,7 @@ async fn end_to_end_retry_test() { .connector(conn.clone()) .middleware(Identity::new()) .retry_config(retry_config) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(); tokio::time::pause(); let initial = tokio::time::Instant::now(); diff --git a/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs b/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs index 475c076b12..695e069cf1 100644 --- a/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs +++ b/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -#![cfg(feature = "test-util")] +#![cfg(feature = "wiremock")] mod test_operation; -use aws_smithy_async::rt::sleep::TokioSleep; +use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_client::test_connection::wire_mock; use aws_smithy_client::test_connection::wire_mock::{check_matches, RecordedEvent, ReplayedEvent}; use aws_smithy_client::{hyper_ext, Builder}; @@ -21,7 +21,6 @@ use http::Uri; use http_body::combinators::BoxBody; use hyper::client::{Builder as HyperBuilder, HttpConnector}; use std::convert::Infallible; -use std::sync::Arc; use std::time::Duration; use test_operation::{TestOperationParser, TestRetryClassifier}; use tower::layer::util::Identity; @@ -94,7 +93,7 @@ async fn wire_level_test( .operation_attempt_timeout(Duration::from_millis(100)) .build(), )) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(); loop { match client diff --git a/rust-runtime/aws-smithy-eventstream/Cargo.toml b/rust-runtime/aws-smithy-eventstream/Cargo.toml index a77aff9e85..c6a3e6782f 100644 --- a/rust-runtime/aws-smithy-eventstream/Cargo.toml +++ b/rust-runtime/aws-smithy-eventstream/Cargo.toml @@ -11,11 +11,11 @@ repository = "https://github.com/awslabs/smithy-rs" derive-arbitrary = ["arbitrary", "derive_arbitrary"] [dependencies] -derive_arbitrary = { version = "=1.1.6", optional = true } # 1.2.0 requires Rust 1.63 to compile -arbitrary = { version = "=1.1.3", optional = true } # 1.1.4 requires Rust 1.63 to compile +arbitrary = { version = "1.3", optional = true } aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" crc32fast = "1.3" +derive_arbitrary = { version = "1.3", optional = true } [dev-dependencies] bytes-utils = "0.1" diff --git a/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml b/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml index d43627339b..f95d493835 100644 --- a/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml +++ b/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml @@ -9,11 +9,11 @@ edition = "2021" cargo-fuzz = true [dependencies] -derive_arbitrary = "=1.1.6" # 1.2.0 requires Rust 1.63 to compile -arbitrary = "=1.1.3" # 1.1.4 requires Rust 1.63 to compile +arbitrary = "1.3" aws-smithy-types = { path = "../../aws-smithy-types" } bytes = "1" crc32fast = "1" +derive_arbitrary = "1.3" libfuzzer-sys = "0.4" [dependencies.aws-smithy-eventstream] diff --git a/rust-runtime/aws-smithy-eventstream/src/frame.rs b/rust-runtime/aws-smithy-eventstream/src/frame.rs index a3d9c29a00..202e410827 100644 --- a/rust-runtime/aws-smithy-eventstream/src/frame.rs +++ b/rust-runtime/aws-smithy-eventstream/src/frame.rs @@ -14,6 +14,7 @@ use std::convert::{TryFrom, TryInto}; use std::error::Error as StdError; use std::fmt; use std::mem::size_of; +use std::sync::{mpsc, Mutex}; const PRELUDE_LENGTH_BYTES: u32 = 3 * size_of::() as u32; const PRELUDE_LENGTH_BYTES_USIZE: usize = PRELUDE_LENGTH_BYTES as usize; @@ -34,6 +35,99 @@ pub trait SignMessage: fmt::Debug { fn sign_empty(&mut self) -> Option>; } +/// A sender that gets placed in the request config to wire up an event stream signer after signing. +#[derive(Debug)] +#[non_exhaustive] +pub struct DeferredSignerSender(Mutex>>); + +impl DeferredSignerSender { + /// Creates a new `DeferredSignerSender` + fn new(tx: mpsc::Sender>) -> Self { + Self(Mutex::new(tx)) + } + + /// Sends a signer on the channel + pub fn send( + &self, + signer: Box, + ) -> Result<(), mpsc::SendError>> { + self.0.lock().unwrap().send(signer) + } +} + +impl Storable for DeferredSignerSender { + type Storer = StoreReplace; +} + +/// Deferred event stream signer to allow a signer to be wired up later. +/// +/// HTTP request signing takes place after serialization, and the event stream +/// message stream body is established during serialization. Since event stream +/// signing may need context from the initial HTTP signing operation, this +/// [`DeferredSigner`] is needed to wire up the signer later in the request lifecycle. +/// +/// This signer basically just establishes a MPSC channel so that the sender can +/// be placed in the request's config. Then the HTTP signer implementation can +/// retrieve the sender from that config and send an actual signing implementation +/// with all the context needed. +/// +/// When an event stream implementation needs to sign a message, the first call to +/// sign will acquire a signing implementation off of the channel and cache it +/// for the remainder of the operation. +#[derive(Debug)] +pub struct DeferredSigner { + rx: Option>>>, + signer: Option>, +} + +impl DeferredSigner { + pub fn new() -> (Self, DeferredSignerSender) { + let (tx, rx) = mpsc::channel(); + ( + Self { + rx: Some(Mutex::new(rx)), + signer: None, + }, + DeferredSignerSender::new(tx), + ) + } + + fn acquire(&mut self) -> &mut (dyn SignMessage + Send + Sync) { + // Can't use `if let Some(signer) = &mut self.signer` because the borrow checker isn't smart enough + if self.signer.is_some() { + return self.signer.as_mut().unwrap().as_mut(); + } else { + self.signer = Some( + self.rx + .take() + .expect("only taken once") + .lock() + .unwrap() + .try_recv() + .ok() + // TODO(enableNewSmithyRuntimeCleanup): When the middleware implementation is removed, + // this should panic rather than default to the `NoOpSigner`. The reason it defaults + // is because middleware-based generic clients don't have any default middleware, + // so there is no way to send a `NoOpSigner` by default when there is no other + // auth scheme. The orchestrator auth setup is a lot more robust and will make + // this problem trivial. + .unwrap_or_else(|| Box::new(NoOpSigner {}) as _), + ); + self.acquire() + } + } +} + +impl SignMessage for DeferredSigner { + fn sign(&mut self, message: Message) -> Result { + self.acquire().sign(message) + } + + fn sign_empty(&mut self) -> Option> { + self.acquire().sign_empty() + } +} + #[derive(Debug)] pub struct NoOpSigner {} impl SignMessage for NoOpSigner { @@ -299,6 +393,7 @@ mod value { } } +use aws_smithy_types::config_bag::{Storable, StoreReplace}; pub use value::HeaderValue; /// Event Stream header. @@ -848,3 +943,56 @@ mod message_frame_decoder_tests { } } } + +#[cfg(test)] +mod deferred_signer_tests { + use crate::frame::{DeferredSigner, Header, HeaderValue, Message, SignMessage}; + use bytes::Bytes; + + fn check_send_sync(value: T) -> T { + value + } + + #[test] + fn deferred_signer() { + #[derive(Default, Debug)] + struct TestSigner { + call_num: i32, + } + impl SignMessage for TestSigner { + fn sign( + &mut self, + message: Message, + ) -> Result { + self.call_num += 1; + Ok(message.add_header(Header::new("call_num", HeaderValue::Int32(self.call_num)))) + } + + fn sign_empty(&mut self) -> Option> { + None + } + } + + let (mut signer, sender) = check_send_sync(DeferredSigner::new()); + + sender.send(Box::::default()).expect("success"); + + let message = signer.sign(Message::new(Bytes::new())).expect("success"); + assert_eq!(1, message.headers()[0].value().as_int32().unwrap()); + + let message = signer.sign(Message::new(Bytes::new())).expect("success"); + assert_eq!(2, message.headers()[0].value().as_int32().unwrap()); + + assert!(signer.sign_empty().is_none()); + } + + #[test] + fn deferred_signer_defaults_to_noop_signer() { + let (mut signer, _sender) = DeferredSigner::new(); + assert_eq!( + Message::new(Bytes::new()), + signer.sign(Message::new(Bytes::new())).unwrap() + ); + assert!(signer.sign_empty().is_none()); + } +} diff --git a/rust-runtime/aws-smithy-http-auth/src/lib.rs b/rust-runtime/aws-smithy-http-auth/src/lib.rs index b245385253..8f5f956ced 100644 --- a/rust-runtime/aws-smithy-http-auth/src/lib.rs +++ b/rust-runtime/aws-smithy-http-auth/src/lib.rs @@ -3,6 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(enableNewSmithyRuntimeCleanup): The contents of this crate are moving into aws-smithy-runtime. +// This crate is kept to continue sorting the middleware implementation until it is removed. +// When removing the old implementation, clear out this crate and deprecate it. + #![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_docs, diff --git a/rust-runtime/aws-smithy-http-server-python/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/Cargo.toml index 69e9577160..61ce57d7a7 100644 --- a/rust-runtime/aws-smithy-http-server-python/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/Cargo.toml @@ -21,29 +21,24 @@ aws-smithy-xml = { path = "../aws-smithy-xml" } bytes = "1.2" futures = "0.3" http = "0.2" -hyper = { version = "0.14.20", features = ["server", "http1", "http2", "tcp", "stream"] } -tls-listener = { version = "0.5.1", features = ["rustls", "hyper-h2"] } +hyper = { version = "0.14.26", features = ["server", "http1", "http2", "tcp", "stream"] } +tls-listener = { version = "0.7.0", features = ["rustls", "hyper-h2"] } rustls-pemfile = "1.0.1" -tokio-rustls = "0.23.4" -lambda_http = { version = "0.7.1" } -# There is a breaking change in `lambda_runtime` between `0.7.0` and `0.7.1`, -# and `lambda_http` depends on `0.7` which by default resolves to `0.7.1` but in our CI -# we are running `minimal-versions` which downgrades `lambda_runtime` to `0.7.0` and fails to compile -# because of the breaking change. Here we are forcing it to use `lambda_runtime = 0.7.1`. -lambda_runtime = { version = "0.7.1" } +tokio-rustls = "0.24.0" +lambda_http = { version = "0.8.0" } num_cpus = "1.13.1" parking_lot = "0.12.1" pin-project-lite = "0.2" -pyo3 = "0.17.0" -pyo3-asyncio = { version = "0.17.0", features = ["tokio-runtime"] } +pyo3 = "0.18.2" +pyo3-asyncio = { version = "0.18.0", features = ["tokio-runtime"] } signal-hook = { version = "0.3.14", features = ["extended-siginfo"] } -socket2 = { version = "0.4.4", features = ["all"] } +socket2 = { version = "0.5.2", features = ["all"] } thiserror = "1.0.32" tokio = { version = "1.20.1", features = ["full"] } tokio-stream = "0.1" tower = { version = "0.4.13", features = ["util"] } tracing = "0.1.36" -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } +tracing-subscriber = { version = "0.3.15", features = ["json", "env-filter"] } tracing-appender = { version = "0.2.2"} [dev-dependencies] @@ -51,9 +46,9 @@ pretty_assertions = "1" futures-util = { version = "0.3.16", default-features = false } tower-test = "0.4" tokio-test = "0.4" -pyo3-asyncio = { version = "0.17.0", features = ["testing", "attributes", "tokio-runtime", "unstable-streams"] } +pyo3-asyncio = { version = "0.18.0", features = ["testing", "attributes", "tokio-runtime", "unstable-streams"] } rcgen = "0.10.0" -hyper-rustls = { version = "0.23.1", features = ["http2"] } +hyper-rustls = { version = "0.24", features = ["http2"] } # PyO3 Asyncio tests cannot use Cargo's default testing harness because `asyncio` # wants to control the main thread. So we need to use testing harness provided by `pyo3_asyncio` diff --git a/rust-runtime/aws-smithy-http-server-python/README.md b/rust-runtime/aws-smithy-http-server-python/README.md index 7b8d218f3c..da3dc2c131 100644 --- a/rust-runtime/aws-smithy-http-server-python/README.md +++ b/rust-runtime/aws-smithy-http-server-python/README.md @@ -2,76 +2,6 @@ Server libraries for smithy-rs generated servers, targeting pure Python business logic. -## Running servers on AWS Lambda - -`aws-smithy-http-server-python` supports running your services on [AWS Lambda](https://aws.amazon.com/lambda/). - -You need to use `run_lambda` method instead of `run` method to start -the [custom runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) -instead of the [Hyper](https://hyper.rs/) HTTP server. - -In your `app.py`: - -```diff -from pokemon_service_server_sdk import App -from pokemon_service_server_sdk.error import ResourceNotFoundException - -# ... - -# Get the number of requests served by this server. -@app.get_server_statistics -def get_server_statistics( - _: GetServerStatisticsInput, context: Context -) -> GetServerStatisticsOutput: - calls_count = context.get_calls_count() - logging.debug("The service handled %d requests", calls_count) - return GetServerStatisticsOutput(calls_count=calls_count) - -# ... - --app.run() -+app.run_lambda() -``` - -`aws-smithy-http-server-python` comes with a -[custom runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) -so you should run your service without any provided runtimes. -You can achieve that with a `Dockerfile` similar to this: - -```dockerfile -# You can use any image that has your desired Python version -FROM public.ecr.aws/lambda/python:3.8-x86_64 - -# Copy your application code to `LAMBDA_TASK_ROOT` -COPY app.py ${LAMBDA_TASK_ROOT} - -# When you build your Server SDK for your service, you will get a Python wheel. -# You just need to copy that wheel and install it via `pip` inside your image. -# Note that you need to build your library for Linux, and Python version used to -# build your SDK should match with your image's Python version. -# For cross compiling, you can consult to: -# https://pyo3.rs/latest/building_and_distribution.html#cross-compiling -COPY wheels/ ${LAMBDA_TASK_ROOT}/wheels -RUN pip3 install ${LAMBDA_TASK_ROOT}/wheels/*.whl - -# You can install your application's other dependencies listed in `requirements.txt`. -COPY requirements.txt . -RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" - -# Create a symlink for your application's entrypoint, -# so we can use `/app.py` to refer it -RUN ln -s ${LAMBDA_TASK_ROOT}/app.py /app.py - -# By default `public.ecr.aws/lambda/python` images comes with Python runtime, -# we need to override `ENTRYPOINT` and `CMD` to not call that runtime and -# instead run directly your service and it will start our custom runtime. -ENTRYPOINT [ "/var/lang/bin/python3.8" ] -CMD [ "/app.py" ] -``` - -See [https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-create-from-base](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-create-from-base) -for more details on building your custom image. - This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. diff --git a/rust-runtime/aws-smithy-http-server-python/examples/README.md b/rust-runtime/aws-smithy-http-server-python/examples/README.md deleted file mode 100644 index f765f4b57b..0000000000 --- a/rust-runtime/aws-smithy-http-server-python/examples/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Smithy Rust/Python Server SDK example - -This folder contains an example service called Pokémon Service used to showcase -the service framework Python bindings capabilities and to run benchmarks. - -The Python implementation of the service can be found inside -[pokemon_service.py](/rust-runtime/aws-smithy-http-python-server/examples/pokemon_service.py). - -## Build - -Since this example requires both the server and client SDK to be code-generated -from their [model](/codegen-server-test/model/pokemon.smithy), a Makefile is -provided to build and run the service. Just run `make build` to prepare the first -build. - -Once the example has been built successfully the first time, idiomatic `cargo` -can be used directly. - -`make distclean` can be used for a complete cleanup of all artefacts. - -### Uvloop - -The server can depend on [uvloop](https://pypi.org/project/uvloop/) for a -faster event loop implementation. Uvloop can be installed with your favourite -package manager or by using pip: - -```sh -pip instal uvloop -``` - -and it will be automatically used instead of the standard library event loop if -it is found in the dependencies' closure. - -### MacOs - -To compile and test on MacOs, please follow the official PyO3 guidelines on how -to [configure your linker](https://pyo3.rs/latest/building_and_distribution.html?highlight=rustflags#macos). - -Please note that the `.cargo/config.toml` with linkers override can be local to -your project. - -## Run - -`cargo run` can be used to start the Pokémon service on -`http://localhost:13734`. - -## Test - -`cargo test` can be used to spawn the Python service and run some simple integration -tests against it. - -More info can be found in the `tests` folder of `pokemon-service-test` package. diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml deleted file mode 100644 index 0efedc3d63..0000000000 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "pokemon-service-test" -version = "0.1.0" -edition = "2021" -publish = false -authors = ["Smithy-rs Server Team "] -description = "Run tests against the Python server implementation" - -[dev-dependencies] -command-group = "1.0" -tokio = { version = "1.20.1", features = ["full"] } -serial_test = "0.9.0" -rustls-pemfile = "1.0.1" -tokio-rustls = "0.23.4" -hyper-rustls = { version = "0.23.0", features = ["http2"] } - -# Local paths -aws-smithy-client = { path = "../../../aws-smithy-client/", features = ["rustls"] } -aws-smithy-http = { path = "../../../aws-smithy-http/" } -aws-smithy-types = { path = "../../../aws-smithy-types/" } -pokemon-service-client = { path = "../pokemon-service-client/" } diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs b/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs deleted file mode 100644 index 34efeba18a..0000000000 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -// Files here are for running integration tests. -// These tests only have access to your crate's public API. -// See: https://doc.rust-lang.org/book/ch11-03-test-organization.html#integration-tests - -use aws_smithy_types::error::display::DisplayErrorContext; -use serial_test::serial; - -use crate::helpers::{client, http2_client, PokemonClient, PokemonService}; - -mod helpers; - -#[tokio::test] -#[serial] -async fn simple_integration_test() { - let _program = PokemonService::run().await; - simple_integration_test_with_client(client()).await; -} - -#[tokio::test] -#[serial] -async fn simple_integration_test_http2() { - let _program = PokemonService::run_http2().await; - simple_integration_test_with_client(http2_client()).await; -} - -async fn simple_integration_test_with_client(client: PokemonClient) { - let service_statistics_out = client.get_server_statistics().send().await.unwrap(); - assert_eq!(0, service_statistics_out.calls_count.unwrap()); - - let pokemon_species_output = client - .get_pokemon_species() - .name("pikachu") - .send() - .await - .unwrap(); - assert_eq!("pikachu", pokemon_species_output.name().unwrap()); - - let service_statistics_out = client.get_server_statistics().send().await.unwrap(); - assert_eq!(1, service_statistics_out.calls_count.unwrap()); - - let pokemon_species_error = client - .get_pokemon_species() - .name("some_pokémon") - .send() - .await - .unwrap_err(); - let message = DisplayErrorContext(pokemon_species_error).to_string(); - let expected = - r#"ResourceNotFoundError [ResourceNotFoundException]: Requested Pokémon not available"#; - assert!( - message.contains(expected), - "expected '{message}' to contain '{expected}'" - ); - - let service_statistics_out = client.get_server_statistics().send().await.unwrap(); - assert_eq!(2, service_statistics_out.calls_count.unwrap()); - - let _health_check = client.check_health().send().await.unwrap(); -} diff --git a/rust-runtime/aws-smithy-http-server-python/src/error.rs b/rust-runtime/aws-smithy-http-server-python/src/error.rs index 01596be507..2cc308fea5 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/error.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/error.rs @@ -7,7 +7,7 @@ use aws_smithy_http_server::{ body::{to_boxed, BoxBody}, - proto::{ + protocol::{ aws_json_10::AwsJson1_0, aws_json_11::AwsJson1_1, rest_json_1::RestJson1, rest_xml::RestXml, }, response::IntoResponse, diff --git a/rust-runtime/aws-smithy-http-server-python/src/logging.rs b/rust-runtime/aws-smithy-http-server-python/src/logging.rs index 80d4966ac0..57fa2873aa 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/logging.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/logging.rs @@ -4,7 +4,8 @@ */ //! Rust `tracing` and Python `logging` setup and utilities. -use std::path::PathBuf; + +use std::{path::PathBuf, str::FromStr}; use pyo3::prelude::*; #[cfg(not(test))] @@ -15,15 +16,49 @@ use tracing_subscriber::{ fmt::{self, writer::MakeWriterExt}, layer::SubscriberExt, util::SubscriberInitExt, + Layer, }; use crate::error::PyException; +#[derive(Debug, Default)] +enum Format { + Json, + Pretty, + #[default] + Compact, +} + +#[derive(Debug, PartialEq, Eq)] +struct InvalidFormatError; + +impl FromStr for Format { + type Err = InvalidFormatError; + + fn from_str(s: &str) -> Result { + match s { + "pretty" => Ok(Self::Pretty), + "json" => Ok(Self::Json), + "compact" => Ok(Self::Compact), + _ => Err(InvalidFormatError), + } + } +} + /// Setup tracing-subscriber to log on console or to a hourly rolling file. fn setup_tracing_subscriber( level: Option, logfile: Option, + format: Option, ) -> PyResult> { + let format = match format { + Some(format) => Format::from_str(&format).unwrap_or_else(|_| { + tracing::error!("unknown format '{format}', falling back to default formatter"); + Format::default() + }), + None => Format::default(), + }; + let appender = match logfile { Some(logfile) => { let parent = logfile.parent().ok_or_else(|| { @@ -54,27 +89,27 @@ fn setup_tracing_subscriber( _ => Level::TRACE, }; + let formatter = fmt::Layer::new().with_line_number(true).with_level(true); + match appender { Some((appender, guard)) => { - let layer = Some( - fmt::Layer::new() - .with_writer(appender.with_max_level(tracing_level)) - .with_ansi(true) - .with_line_number(true) - .with_level(true), - ); - tracing_subscriber::registry().with(layer).init(); + let formatter = formatter.with_writer(appender.with_max_level(tracing_level)); + let formatter = match format { + Format::Json => formatter.json().boxed(), + Format::Compact => formatter.compact().boxed(), + Format::Pretty => formatter.pretty().boxed(), + }; + tracing_subscriber::registry().with(formatter).init(); Ok(Some(guard)) } None => { - let layer = Some( - fmt::Layer::new() - .with_writer(std::io::stdout.with_max_level(tracing_level)) - .with_ansi(true) - .with_line_number(true) - .with_level(true), - ); - tracing_subscriber::registry().with(layer).init(); + let formatter = formatter.with_writer(std::io::stdout.with_max_level(tracing_level)); + let formatter = match format { + Format::Json => formatter.json().boxed(), + Format::Compact => formatter.compact().boxed(), + Format::Pretty => formatter.pretty().boxed(), + }; + tracing_subscriber::registry().with(formatter).init(); Ok(None) } } @@ -89,9 +124,10 @@ fn setup_tracing_subscriber( /// /// :param level typing.Optional\[int\]: /// :param logfile typing.Optional\[pathlib.Path\]: +/// :param format typing.Optional\[typing.Literal\['compact', 'pretty', 'json'\]\]: /// :rtype None: #[pyclass(name = "TracingHandler")] -#[pyo3(text_signature = "($self, level=None, logfile=None)")] +#[pyo3(text_signature = "($self, level=None, logfile=None, format=None)")] #[derive(Debug)] pub struct PyTracingHandler { _guard: Option, @@ -100,8 +136,13 @@ pub struct PyTracingHandler { #[pymethods] impl PyTracingHandler { #[new] - fn newpy(py: Python, level: Option, logfile: Option) -> PyResult { - let _guard = setup_tracing_subscriber(level, logfile)?; + fn newpy( + py: Python, + level: Option, + logfile: Option, + format: Option, + ) -> PyResult { + let _guard = setup_tracing_subscriber(level, logfile, format)?; let logging = py.import("logging")?; let root = logging.getattr("root")?; root.setattr("level", level)?; @@ -190,7 +231,7 @@ mod tests { fn tracing_handler_is_injected_in_python() { crate::tests::initialize(); Python::with_gil(|py| { - let handler = PyTracingHandler::newpy(py, Some(10), None).unwrap(); + let handler = PyTracingHandler::newpy(py, Some(10), None, None).unwrap(); let kwargs = PyDict::new(py); kwargs .set_item("handlers", vec![handler.handler(py).unwrap()]) diff --git a/rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs b/rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs index 506e5a6c20..cf6cb2d095 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs @@ -7,7 +7,7 @@ use std::convert::Infallible; use aws_smithy_http_server::{ body::{to_boxed, Body, BoxBody}, - proto::rest_json_1::RestJson1, + protocol::rest_json_1::RestJson1, }; use aws_smithy_http_server_python::{ middleware::{PyMiddlewareHandler, PyMiddlewareLayer}, diff --git a/rust-runtime/aws-smithy-http-server-python/src/tls.rs b/rust-runtime/aws-smithy-http-server-python/src/tls.rs index 538508fcec..c817cabaca 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/tls.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/tls.rs @@ -105,7 +105,7 @@ impl PyTlsConfig { #[pymethods] impl PyTlsConfig { #[new] - #[args(reload_secs = "86400")] // <- 1 Day by default + #[pyo3(signature = (key_path, cert_path, reload_secs=86400))] fn py_new(key_path: PathBuf, cert_path: PathBuf, reload_secs: u64) -> Self { // TODO(BugOnUpstream): `reload: &PyDelta` segfaults, create an issue on PyO3 Self { @@ -146,11 +146,11 @@ mod tests { const TEST_KEY: &str = concat!( env!("CARGO_MANIFEST_DIR"), - "/examples/pokemon-service-test/tests/testdata/localhost.key" + "/../../examples/python/pokemon-service-test/tests/testdata/localhost.key" ); const TEST_CERT: &str = concat!( env!("CARGO_MANIFEST_DIR"), - "/examples/pokemon-service-test/tests/testdata/localhost.crt" + "/../../examples/python/pokemon-service-test/tests/testdata/localhost.crt" ); #[test] diff --git a/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs b/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs index fb4f759f89..9e6dbed364 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs @@ -144,7 +144,7 @@ mod tests { assert!(response .unwrap_err() .to_string() - .contains("invalid peer certificate: UnknownIssuer")); + .contains("invalid peer certificate")); } { @@ -191,7 +191,7 @@ mod tests { assert!(response .unwrap_err() .to_string() - .contains("invalid peer certificate: InvalidCertValidity")); + .contains("invalid peer certificate: Expired")); } // Make a new acceptor with a valid cert and replace diff --git a/rust-runtime/aws-smithy-http-server-python/examples/stubgen.py b/rust-runtime/aws-smithy-http-server-python/stubgen.py similarity index 88% rename from rust-runtime/aws-smithy-http-server-python/examples/stubgen.py rename to rust-runtime/aws-smithy-http-server-python/stubgen.py index 30348838d2..0edf1d3ef2 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/stubgen.py +++ b/rust-runtime/aws-smithy-http-server-python/stubgen.py @@ -2,11 +2,12 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -import re + import inspect +import re import textwrap from pathlib import Path -from typing import Any, Set, Dict, List, Tuple, Optional +from typing import Any, Dict, List, Optional, Set, Tuple ROOT_MODULE_NAME_PLACEHOLDER = "__root_module_name__" @@ -36,11 +37,7 @@ def fix_path(self, path: str) -> str: Returns fixed version of given type path. It unescapes `\\[` and `\\]` and also populates placeholder for root module name. """ - return ( - path.replace(ROOT_MODULE_NAME_PLACEHOLDER, self.root_module_name) - .replace("\\[", "[") - .replace("\\]", "]") - ) + return path.replace(ROOT_MODULE_NAME_PLACEHOLDER, self.root_module_name).replace("\\[", "[").replace("\\]", "]") def submodule(self, path: Path) -> Writer: w = Writer(path, self.root_module_name) @@ -98,27 +95,21 @@ def __init__(self) -> None: def parse_type_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=1) if len(parts) != 2: - raise ValueError( - f"Invalid `:type` directive: `{line}` must be in `:type T:` format" - ) + raise ValueError(f"Invalid `:type` directive: `{line}` must be in `:type T:` format") res.types.append(parts[1].rstrip(":")) def parse_rtype_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=1) if len(parts) != 2: - raise ValueError( - f"Invalid `:rtype` directive: `{line}` must be in `:rtype T:` format" - ) + raise ValueError(f"Invalid `:rtype` directive: `{line}` must be in `:rtype T:` format") res.rtypes.append(parts[1].rstrip(":")) def parse_param_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=2) if len(parts) != 3: - raise ValueError( - f"Invalid `:param` directive: `{line}` must be in `:param name T:` format" - ) + raise ValueError(f"Invalid `:param` directive: `{line}` must be in `:param name T:` format") name = parts[1] ty = parts[2].rstrip(":") res.params.append((name, ty)) @@ -127,18 +118,14 @@ def parse_param_directive(line: str, res: DocstringParserResult): def parse_generic_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=1) if len(parts) != 2: - raise ValueError( - f"Invalid `:generic` directive: `{line}` must be in `:generic T:` format" - ) + raise ValueError(f"Invalid `:generic` directive: `{line}` must be in `:generic T:` format") res.generics.append(parts[1].rstrip(":")) def parse_extends_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=1) if len(parts) != 2: - raise ValueError( - f"Invalid `:extends` directive: `{line}` must be in `:extends Base[...]:` format" - ) + raise ValueError(f"Invalid `:extends` directive: `{line}` must be in `:extends Base[...]:` format") res.extends.append(parts[1].rstrip(":")) @@ -201,13 +188,13 @@ def clean_doc(obj: Any) -> str: if not doc: return "" - def predicate(l: str) -> bool: + def predicate(line: str) -> bool: for k in DocstringParserDirectives.keys(): - if l.startswith(f":{k} ") and l.endswith(":"): + if line.startswith(f":{k} ") and line.endswith(":"): return False return True - return "\n".join([l for l in doc.splitlines() if predicate(l)]).strip() + return "\n".join([line for line in doc.splitlines() if predicate(line)]).strip() def indent(code: str, level: int = 4) -> str: @@ -225,6 +212,10 @@ def is_fn_like(obj: Any) -> bool: ) +def is_scalar(obj: Any) -> bool: + return isinstance(obj, (str, float, int, bool)) + + def join(args: List[str], delim: str = "\n") -> str: return delim.join(filter(lambda x: x, args)) @@ -266,7 +257,7 @@ def make_function( sig: Optional[inspect.Signature] = None try: sig = inspect.signature(obj) - except: + except Exception: pass def has_default(param: str, ty: str) -> bool: @@ -312,9 +303,7 @@ def {name}({params}) -> {rtype}: def make_class(writer: Writer, name: str, klass: Any) -> str: - bases = list( - filter(lambda n: n != "object", map(lambda b: b.__name__, klass.__bases__)) - ) + bases = list(filter(lambda n: n != "object", map(lambda b: b.__name__, klass.__bases__))) class_sig = DocstringParser.parse_class(klass) if class_sig: (generics, extends) = class_sig @@ -386,7 +375,7 @@ class {name}{bases_str}: def walk_module(writer: Writer, mod: Any): exported = mod.__all__ - for (name, member) in inspect.getmembers(mod): + for name, member in inspect.getmembers(mod): if name not in exported: continue @@ -397,10 +386,25 @@ def walk_module(writer: Writer, mod: Any): writer.define(make_class(writer, name, member)) elif is_fn_like(member): writer.define(make_function(writer, name, member)) + elif is_scalar(member): + writer.define(f"{name}: {type(member).__name__} = ...") else: print(f"Unknown type: {member}") +def generate(module: str, outdir: str): + path = Path(outdir) / "__init__.pyi" + writer = Writer( + path, + module, + ) + walk_module( + writer, + importlib.import_module(module), + ) + writer.dump() + + if __name__ == "__main__": import argparse import importlib @@ -410,13 +414,4 @@ def walk_module(writer: Writer, mod: Any): parser.add_argument("outdir") args = parser.parse_args() - path = Path(args.outdir) / f"{args.module}.pyi" - writer = Writer( - path, - args.module, - ) - walk_module( - writer, - importlib.import_module(args.module), - ) - writer.dump() + generate(args.module, args.outdir) diff --git a/rust-runtime/aws-smithy-http-server-python/stubgen.sh b/rust-runtime/aws-smithy-http-server-python/stubgen.sh new file mode 100755 index 0000000000..f7708845b3 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-python/stubgen.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -x + +if [ $# -lt 3 ]; then + echo "usage: $0 package manifest_path output_directory" + exit 1 +fi + +# input arguments +package=$1 +manifest=$2 +output=$3 + +# the directory of the script +source_dir="$(git rev-parse --show-toplevel)" +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [ -n "$source_dir" ]; then + CARGO_TARGET_DIR="$source_dir/target" +else + CARGO_TARGET_DIR=$(mktemp -d) + mkdir -p "$CARGO_TARGET_DIR" + # cleanup temporary directory + function cleanup { + # shellcheck disable=2317 + rm -rf "$CARGO_TARGET_DIR" + } + # register the cleanup function to be called on the EXIT signal + trap cleanup EXIT +fi +export CARGO_TARGET_DIR + +shared_object_extension="so" +# generate the Python stubs, +if [ "$(uname)" == "Darwin" ]; then + shared_object_extension="dylib" + export CARGO_TARGET_X86_64_APPLE_DARWIN_RUSTFLAGS="-C link-arg=-undefined -C link-arg=dynamic_lookup" + export CARGO_TARGET_AARCH64_APPLE_DARWIN_RUSTFLAGS="-C link-arg=-undefined -C link-arg=dynamic_lookup" +fi + +cargo build --manifest-path "$manifest" +# The link target have to end with .so to be sure it is importable by the stubgen.py script. +ln -sf "$CARGO_TARGET_DIR/debug/lib$package.$shared_object_extension" "$CARGO_TARGET_DIR/debug/$package.so" +PYTHONPATH=$CARGO_TARGET_DIR/debug:$PYTHONPATH python3 "$script_dir/stubgen.py" "$package" "$output" + +exit 0 diff --git a/rust-runtime/aws-smithy-http-server-python/examples/stubgen_test.py b/rust-runtime/aws-smithy-http-server-python/stubgen_test.py similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/stubgen_test.py rename to rust-runtime/aws-smithy-http-server-python/stubgen_test.py diff --git a/rust-runtime/aws-smithy-http-server-typescript/Cargo.toml b/rust-runtime/aws-smithy-http-server-typescript/Cargo.toml new file mode 100644 index 0000000000..1147d0da98 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "aws-smithy-http-server-typescript" +version = "0.0.0-smithy-rs-head" +authors = ["Smithy Rust Server "] +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" +keywords = ["smithy", "framework", "web", "api", "aws", "typescript"] +categories = ["asynchronous", "web-programming", "api-bindings"] +description = """ +Typescript server runtime for Smithy Rust Server Framework. +""" +publish = false + +[dependencies] + +[build-dependencies] + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--cfg", "docsrs"] +# End of docs.rs metadata diff --git a/rust-runtime/aws-smithy-http-server-typescript/LICENSE b/rust-runtime/aws-smithy-http-server-typescript/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/rust-runtime/aws-smithy-http-server-typescript/README.md b/rust-runtime/aws-smithy-http-server-typescript/README.md new file mode 100644 index 0000000000..b5d516d1c2 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/README.md @@ -0,0 +1,7 @@ +# aws-smithy-http-server-typescript + +Server libraries for smithy-rs generated servers, targeting pure Typescript business logic. + + +This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. + diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/.gitignore b/rust-runtime/aws-smithy-http-server-typescript/examples/.gitignore new file mode 100644 index 0000000000..7f71e8f1ca --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/.gitignore @@ -0,0 +1,93 @@ +pokemon-service-client/ +pokemon-service-server-sdk/ +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions +$RECYCLE.BIN/ +**/*.rs.bk +*.cab +*.icloud +*.lcov +*.lnk +*.log +*.msi +*.msix +*.msm +*.msp +*.node +*.pid +*.pid.lock +*.seed +*.stackdump +*.tgz +*.tsbuildinfo +.AppleDB +.AppleDesktop +.AppleDouble +.DS_Store +.DocumentRevisions-V100 +.LSOverride +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +._* +.apdisk +.cache +.cache/ +.com.apple.timemachine.donotpresent +.dynamodb/ +.env +.env.test +.eslintcache +.fseventsd +.fusebox/ +.grunt +.lock-wscript +.next +.node_repl_history +.npm +.nuxt +.nyc_output +.pnp.* +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ +.serverless/ +.tern-port +.vscode-test +.vuepress/dist +.yarn-integrity +.yarn/* +/target +Cargo.lock +Icon +Network Trash Folder +Temporary Items +Thumbs.db +Thumbs.db:encryptable +[Dd]esktop.ini +bower_components +build/Release +coverage +dist +ehthumbs.db +ehthumbs_vista.db +jspm_packages/ +lerna-debug.log* +lib-cov +logs +node_modules/ +npm-debug.log* +pids +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json +typings/ +yarn-debug.log* +yarn-error.log* +package-lock.json +index.d.ts +index.js +pokemon-service-server-sdk.*.node diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/Cargo.toml b/rust-runtime/aws-smithy-http-server-typescript/examples/Cargo.toml new file mode 100644 index 0000000000..95b64ff739 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/Cargo.toml @@ -0,0 +1,9 @@ +# Without this configuration, the workspace will be read from `rust-runtime`, causing the build to fail. +[workspace] +members = [ + "pokemon-service-server-sdk", + "pokemon-service-client" +] + +[profile.release] +lto = true diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/Makefile b/rust-runtime/aws-smithy-http-server-typescript/examples/Makefile new file mode 100644 index 0000000000..f199ac1120 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/Makefile @@ -0,0 +1,44 @@ +SRC_DIR := $(shell git rev-parse --show-toplevel) +CUR_DIR := $(shell pwd) +GRADLE := $(SRC_DIR)/gradlew +SERVER_SDK_DST := $(CUR_DIR)/pokemon-service-server-sdk +CLIENT_SDK_DST := $(CUR_DIR)/pokemon-service-client +SERVER_SDK_SRC := $(SRC_DIR)/codegen-server-test/typescript/build/smithyprojections/codegen-server-test-typescript/pokemon-service-server-sdk/rust-server-codegen-typescript +CLIENT_SDK_SRC := $(SRC_DIR)/codegen-client-test/build/smithyprojections/codegen-client-test/pokemon-service-client/rust-client-codegen + +all: codegen + +codegen: + $(GRADLE) --project-dir $(SRC_DIR) -P modules='pokemon-service-server-sdk,pokemon-service-client' :codegen-client-test:assemble :codegen-server-test:typescript:assemble + mkdir -p $(SERVER_SDK_DST) $(CLIENT_SDK_DST) + cp -av $(SERVER_SDK_SRC)/* $(SERVER_SDK_DST)/ + cp -av $(CLIENT_SDK_SRC)/* $(CLIENT_SDK_DST)/ + +build: + cd pokemon-service-server-sdk && npm run build:debug + ln -sf $(shell find pokemon-service-server-sdk -name '*.node') . + ln -sf pokemon-service-server-sdk/index.d.ts + ln -sf pokemon-service-server-sdk/index.js + +release: + cd pokemon-service-server-sdk && npm run build + ln -sf $(shell find pokemon-service-server-sdk -name '*.node') . + ln -sf pokemon-service-server-sdk/index.d.ts + ln -sf pokemon-service-server-sdk/index.js + +run: build + ts-node pokemon-service.ts + +run-release: release + ts-node pokemon-service.ts + +doc-open: codegen + cargo doc --no-deps --open + +clean: + cargo clean || echo "Unable to run cargo clean" + +distclean: clean + rm -rf $(SERVER_SDK_DST) $(CLIENT_SDK_DST) $(CUR_DIR)/Cargo.lock pokemon-service-server-sdk.*.node index.d.ts + +.PHONY: all diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/README.md b/rust-runtime/aws-smithy-http-server-typescript/examples/README.md new file mode 100644 index 0000000000..8f59208ff8 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/README.md @@ -0,0 +1,30 @@ +# Smithy Rust/Typescript Server SDK example + +This folder contains an example service called Pokémon Service used to showcase +the service framework Typescript bindings capabilities and to run benchmarks. + +The Python implementation of the service can be found inside +[pokemon_service.ts](/rust-runtime/aws-smithy-http-typescript-server/examples/pokemon_service.ts). + +## Depedencies + +TODO: Add NAPI installation instructions + +## Build + +Since this example requires both the server and client SDK to be code-generated +from their [model](/codegen-server-test/model/pokemon.smithy), a Makefile is +provided to build and run the service. Just run `make build` to prepare the first +build. + +Once the example has been built successfully the first time, idiomatic `cargo` +can be used directly. + +`make distclean` can be used for a complete cleanup of all artefacts. + +## Test + +`cargo test` can be used to spawn the Python service and run some simple integration +tests against it. + +More info can be found in the `tests` folder of `pokemon-service-test` package. diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/package.json b/rust-runtime/aws-smithy-http-server-typescript/examples/package.json new file mode 100644 index 0000000000..a311cbd557 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "@types/node": "^18.15.7" + } +} diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/pokemon-service.ts b/rust-runtime/aws-smithy-http-server-typescript/examples/pokemon-service.ts new file mode 100644 index 0000000000..269753c703 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/pokemon-service.ts @@ -0,0 +1,97 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import cluster from "cluster"; +import { cpus } from "os"; + +import { + App, + TsHandlers, + GetPokemonSpeciesInput, + GetPokemonSpeciesOutput, + Language, + DoNothingInput, + DoNothingOutput, + TsSocket, + CheckHealthOutput, + CheckHealthInput, + GetServerStatisticsInput, + GetServerStatisticsOutput, +} from "."; + +class HandlerImpl implements TsHandlers { + // TODO: implement + async doNothing(input: DoNothingInput): Promise { + return {}; + } + // TODO: implement + async checkHealth(input: CheckHealthInput): Promise { + return {}; + } + // TODO: implement + async getServerStatistics( + input: GetServerStatisticsInput + ): Promise { + return { callsCount: 0 }; + } + async getPokemonSpecies( + input: GetPokemonSpeciesInput, + ): Promise { + return { + name: input.name, + flavorTextEntries: [ + { + language: Language.English, + flavorText: + "When several of these Pokémon gather, their electricity could build and cause lightning storms.", + }, + { + language: Language.Italian, + flavorText: + "Quando vari Pokémon di questo tipo si radunano, la loro energia può causare forti tempeste.", + }, + { + language: Language.Spanish, + flavorText: + "Cuando varios de estos Pokémon se juntan, su energía puede causar fuertes tormentas.", + }, + { + language: Language.Japanese, + flavorText: + "ほっぺたの りょうがわに ちいさい でんきぶくろを もつ。ピンチのときに ほうでんする。", + }, + ], + }; + } +} + +// Pass the handlers to the App. +const app = new App(new HandlerImpl()); +// Start the app 🤘 +const numCPUs = cpus().length / 2; +const address ="127.0.0.1"; +const port = 9090; +const socket = new TsSocket(address, port); +app.start(socket); + +// TODO: This part should be abstracted out and done directly in Rust. +// We could take an optional number of workers and the socket as input +// of the App.start() method. +if (cluster.isPrimary) { + console.log(`Listening on ${address}:${port}`); + // Fork workers. + for (let i = 0; i < numCPUs; i++) { + cluster.fork(); + } +} + +process.on("unhandledRejection", err => { + console.error("Unhandled") + console.error(err) +}) +process.on("uncaughtException", err => { + console.error("Uncaught") + console.error(err) +}) diff --git a/rust-runtime/aws-smithy-http-server-typescript/src/lib.rs b/rust-runtime/aws-smithy-http-server-typescript/src/lib.rs new file mode 100644 index 0000000000..35d0f3613a --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/src/lib.rs @@ -0,0 +1,4 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index e5226e910a..f1cd62c2d4 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -18,29 +18,28 @@ unredacted-logging = [] request-id = ["dep:uuid"] [dependencies] +async-trait = "0.1" aws-smithy-http = { path = "../aws-smithy-http", features = ["rt-tokio"] } -aws-smithy-types = { path = "../aws-smithy-types" } aws-smithy-json = { path = "../aws-smithy-json" } +aws-smithy-types = { path = "../aws-smithy-types" } aws-smithy-xml = { path = "../aws-smithy-xml" } -async-trait = "0.1" bytes = "1.1" futures-util = { version = "0.3.16", default-features = false } http = "0.2" http-body = "0.4" -hyper = { version = "0.14.12", features = ["server", "http1", "http2", "tcp", "stream"] } -lambda_http = { version = "0.7.1", optional = true } -mime = "0.3" +hyper = { version = "0.14.26", features = ["server", "http1", "http2", "tcp", "stream"] } +lambda_http = { version = "0.8.0", optional = true } +mime = "0.3.4" nom = "7" -pin-project-lite = "0.2" once_cell = "1.13" +pin-project-lite = "0.2" regex = "1.5.5" serde_urlencoded = "0.7" -strum_macros = "0.24" -thiserror = "1.0.0" -tracing = "0.1.35" +thiserror = "1.0.40" tokio = { version = "1.23.1", features = ["full"] } tower = { version = "0.4.11", features = ["util", "make"], default-features = false } tower-http = { version = "0.3", features = ["add-extension", "map-response-body"] } +tracing = "0.1.35" uuid = { version = "1", features = ["v4", "fast-rng"], optional = true } [dev-dependencies] diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 2a8b664cdd..17ff01e961 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -19,15 +19,17 @@ //! //! [extensions]: https://docs.rs/http/latest/http/struct.Extensions.html -use std::{fmt, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; +use std::hash::Hash; +use std::{fmt, fmt::Debug, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; use futures_util::ready; use futures_util::TryFuture; use thiserror::Error; -use tower::{layer::util::Stack, Layer, Service}; +use tower::Service; -use crate::operation::{Operation, OperationShape}; -use crate::plugin::{plugin_from_operation_name_fn, OperationNameFn, Plugin, PluginPipeline, PluginStack}; +use crate::operation::OperationShape; +use crate::plugin::{HttpMarker, HttpPlugins, Plugin, PluginStack}; +use crate::shape_id::ShapeId; pub use crate::request::extension::{Extension, MissingExtension}; @@ -35,13 +37,8 @@ pub use crate::request::extension::{Extension, MissingExtension}; /// This extension type is inserted, via the [`OperationExtensionPlugin`], whenever it has been correctly determined /// that the request should be routed to a particular operation. The operation handler might not even get invoked /// because the request fails to deserialize into the modeled operation input. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct OperationExtension { - absolute: &'static str, - - namespace: &'static str, - name: &'static str, -} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct OperationExtension(pub ShapeId); /// An error occurred when parsing an absolute operation shape ID. #[derive(Debug, Clone, Error, PartialEq, Eq)] @@ -51,36 +48,6 @@ pub enum ParseError { MissingNamespace, } -#[allow(deprecated)] -impl OperationExtension { - /// Creates a new [`OperationExtension`] from the absolute shape ID. - pub fn new(absolute_operation_id: &'static str) -> Result { - let (namespace, name) = absolute_operation_id - .rsplit_once('#') - .ok_or(ParseError::MissingNamespace)?; - Ok(Self { - absolute: absolute_operation_id, - namespace, - name, - }) - } - - /// Returns the Smithy model namespace. - pub fn namespace(&self) -> &'static str { - self.namespace - } - - /// Returns the Smithy operation name. - pub fn name(&self) -> &'static str { - self.name - } - - /// Returns the absolute operation shape ID. - pub fn absolute(&self) -> &'static str { - self.absolute - } -} - pin_project_lite::pin_project! { /// The [`Service::Future`] of [`OperationExtensionService`] - inserts an [`OperationExtension`] into the /// [`http::Response]`. @@ -138,23 +105,8 @@ where } } -/// A [`Layer`] applying the [`OperationExtensionService`] to an inner [`Service`]. -#[derive(Debug, Clone)] -pub struct OperationExtensionLayer(OperationExtension); - -impl Layer for OperationExtensionLayer { - type Service = OperationExtensionService; - - fn layer(&self, inner: S) -> Self::Service { - OperationExtensionService { - inner, - operation_extension: self.0.clone(), - } - } -} - -/// A [`Plugin`] which applies [`OperationExtensionLayer`] to every operation. -pub struct OperationExtensionPlugin(OperationNameFn OperationExtensionLayer>); +/// A [`Plugin`] which applies [`OperationExtensionService`] to every operation. +pub struct OperationExtensionPlugin; impl fmt::Debug for OperationExtensionPlugin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -162,33 +114,33 @@ impl fmt::Debug for OperationExtensionPlugin { } } -impl Plugin for OperationExtensionPlugin +impl Plugin for OperationExtensionPlugin where Op: OperationShape, { - type Service = S; - type Layer = Stack; + type Output = OperationExtensionService; - fn map(&self, input: Operation) -> Operation { - OperationExtensionLayer> as Plugin>::map(&self.0, input) + fn apply(&self, inner: T) -> Self::Output { + OperationExtensionService { + inner, + operation_extension: OperationExtension(Op::ID), + } } } -/// An extension trait on [`PluginPipeline`] allowing the application of [`OperationExtensionPlugin`]. +impl HttpMarker for OperationExtensionPlugin {} + +/// An extension trait on [`HttpPlugins`] allowing the application of [`OperationExtensionPlugin`]. /// /// See [`module`](crate::extension) documentation for more info. -pub trait OperationExtensionExt

{ +pub trait OperationExtensionExt { /// Apply the [`OperationExtensionPlugin`], which inserts the [`OperationExtension`] into every [`http::Response`]. - fn insert_operation_extension(self) -> PluginPipeline>; + fn insert_operation_extension(self) -> HttpPlugins>; } -impl

OperationExtensionExt

for PluginPipeline

{ - fn insert_operation_extension(self) -> PluginPipeline> { - let plugin = OperationExtensionPlugin(plugin_from_operation_name_fn(|name| { - let operation_extension = OperationExtension::new(name).expect("Operation name is malformed, this should never happen. Please file an issue against https://github.com/awslabs/smithy-rs"); - OperationExtensionLayer(operation_extension) - })); - self.push(plugin) +impl OperationExtensionExt for HttpPlugins { + fn insert_operation_extension(self) -> HttpPlugins> { + self.push(OperationExtensionPlugin) } } @@ -234,37 +186,36 @@ impl Deref for RuntimeErrorExtension { #[cfg(test)] mod tests { - use tower::{service_fn, ServiceExt}; + use tower::{service_fn, Layer, ServiceExt}; - use crate::{operation::OperationShapeExt, proto::rest_json_1::RestJson1}; + use crate::{plugin::PluginLayer, protocol::rest_json_1::RestJson1}; use super::*; #[test] fn ext_accept() { let value = "com.amazonaws.ebs#CompleteSnapshot"; - let ext = OperationExtension::new(value).unwrap(); + let ext = ShapeId::new( + "com.amazonaws.ebs#CompleteSnapshot", + "com.amazonaws.ebs", + "CompleteSnapshot", + ); assert_eq!(ext.absolute(), value); assert_eq!(ext.namespace(), "com.amazonaws.ebs"); assert_eq!(ext.name(), "CompleteSnapshot"); } - #[test] - fn ext_reject() { - let value = "CompleteSnapshot"; - assert_eq!( - OperationExtension::new(value).unwrap_err(), - ParseError::MissingNamespace - ) - } - #[tokio::test] async fn plugin() { struct DummyOp; impl OperationShape for DummyOp { - const NAME: &'static str = "com.amazonaws.ebs#CompleteSnapshot"; + const ID: ShapeId = ShapeId::new( + "com.amazonaws.ebs#CompleteSnapshot", + "com.amazonaws.ebs", + "CompleteSnapshot", + ); type Input = (); type Output = (); @@ -272,19 +223,17 @@ mod tests { } // Apply `Plugin`. - let operation = DummyOp::from_handler(|_| async { Ok(()) }); - let plugins = PluginPipeline::new().insert_operation_extension(); - let op = Plugin::::map(&plugins, operation); + let plugins = HttpPlugins::new().insert_operation_extension(); // Apply `Plugin`s `Layer`. - let layer = op.layer; + let layer = PluginLayer::new::(plugins); let svc = service_fn(|_: http::Request<()>| async { Ok::<_, ()>(http::Response::new(())) }); let svc = layer.layer(svc); // Check for `OperationExtension`. let response = svc.oneshot(http::Request::new(())).await.unwrap(); - let expected = OperationExtension::new(DummyOp::NAME).unwrap(); + let expected = DummyOp::ID; let actual = response.extensions().get::().unwrap(); - assert_eq!(*actual, expected); + assert_eq!(actual.0, expected); } } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs deleted file mode 100644 index 956fd6b24d..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use tower::Layer; - -use super::{InstrumentOperation, MakeIdentity}; - -/// A [`Layer`] used to apply [`InstrumentOperation`]. -#[derive(Debug)] -pub struct InstrumentLayer { - operation_name: &'static str, - make_request: RequestMakeFmt, - make_response: ResponseMakeFmt, -} - -impl InstrumentLayer { - /// Constructs a new [`InstrumentLayer`] with no data redacted. - pub fn new(operation_name: &'static str) -> Self { - Self { - operation_name, - make_request: MakeIdentity, - make_response: MakeIdentity, - } - } -} - -impl InstrumentLayer { - /// Configures the request format. - /// - /// The argument is typically [`RequestFmt`](super::sensitivity::RequestFmt). - pub fn request_fmt(self, make_request: R) -> InstrumentLayer { - InstrumentLayer { - operation_name: self.operation_name, - make_request, - make_response: self.make_response, - } - } - - /// Configures the response format. - /// - /// The argument is typically [`ResponseFmt`](super::sensitivity::ResponseFmt). - pub fn response_fmt(self, make_response: R) -> InstrumentLayer { - InstrumentLayer { - operation_name: self.operation_name, - make_request: self.make_request, - make_response, - } - } -} - -impl Layer for InstrumentLayer -where - RequestMakeFmt: Clone, - ResponseMakeFmt: Clone, -{ - type Service = InstrumentOperation; - - fn layer(&self, service: S) -> Self::Service { - InstrumentOperation::new(service, self.operation_name) - .request_fmt(self.make_request.clone()) - .response_fmt(self.make_response.clone()) - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs index 72fd2af2e6..bad93a9ae1 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs @@ -13,6 +13,7 @@ //! ``` //! # use std::convert::Infallible; //! # use aws_smithy_http_server::instrumentation::{*, sensitivity::{*, headers::*, uri::*}}; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # use http::{Request, Response}; //! # use tower::{util::service_fn, Service}; //! # async fn service(request: Request<()>) -> Result, Infallible> { @@ -20,6 +21,7 @@ //! # } //! # async fn example() { //! # let service = service_fn(service); +//! # const ID: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); //! let request = Request::get("http://localhost/a/b/c/d?bar=hidden") //! .header("header-name-a", "hidden") //! .body(()) @@ -47,7 +49,7 @@ //! } //! }) //! .status_code(); -//! let mut service = InstrumentOperation::new(service, "foo-operation") +//! let mut service = InstrumentOperation::new(service, ID) //! .request_fmt(request_fmt) //! .response_fmt(response_fmt); //! @@ -57,14 +59,12 @@ //! //! [sensitive trait]: https://awslabs.github.io/smithy/1.0/spec/core/documentation-traits.html?highlight=sensitive%20trait#sensitive-trait -mod layer; mod plugin; pub mod sensitivity; mod service; use std::fmt::{Debug, Display}; -pub use layer::*; pub use plugin::*; pub use service::*; diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs index ce77218603..f2dcca9cac 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs @@ -3,47 +3,43 @@ * SPDX-License-Identifier: Apache-2.0 */ -use tower::layer::util::Stack; +use crate::plugin::{HttpMarker, HttpPlugins, PluginStack}; +use crate::{operation::OperationShape, plugin::Plugin}; -use crate::plugin::{PluginPipeline, PluginStack}; -use crate::{ - operation::{Operation, OperationShape}, - plugin::Plugin, -}; +use super::sensitivity::Sensitivity; +use super::InstrumentOperation; -use super::{layer::InstrumentLayer, sensitivity::Sensitivity}; - -/// A [`Plugin`] which applies [`InstrumentLayer`] to all operations in the builder. +/// A [`Plugin`] which applies [`InstrumentOperation`] to every operation. #[derive(Debug)] pub struct InstrumentPlugin; -impl Plugin for InstrumentPlugin +impl Plugin for InstrumentPlugin where Op: OperationShape, Op: Sensitivity, { - type Service = S; - type Layer = Stack>; + type Output = InstrumentOperation; - fn map(&self, operation: Operation) -> Operation { - let layer = InstrumentLayer::new(Op::NAME) + fn apply(&self, input: T) -> Self::Output { + InstrumentOperation::new(input, Op::ID) .request_fmt(Op::request_fmt()) - .response_fmt(Op::response_fmt()); - operation.layer(layer) + .response_fmt(Op::response_fmt()) } } -/// An extension trait for applying [`InstrumentLayer`] to all operations in a service. -pub trait InstrumentExt { - /// Applies an [`InstrumentLayer`] to all operations which respects the [@sensitive] trait given on the input and +impl HttpMarker for InstrumentPlugin {} + +/// An extension trait for applying [`InstrumentPlugin`]. +pub trait InstrumentExt { + /// Applies an [`InstrumentOperation`] to every operation, respecting the [@sensitive] trait given on the input and /// output models. See [`InstrumentOperation`](super::InstrumentOperation) for more information. /// /// [@sensitive]: https://awslabs.github.io/smithy/2.0/spec/documentation-traits.html#sensitive-trait - fn instrument(self) -> PluginPipeline>; + fn instrument(self) -> HttpPlugins>; } -impl InstrumentExt for PluginPipeline { - fn instrument(self) -> PluginPipeline> { +impl InstrumentExt for HttpPlugins { + fn instrument(self) -> HttpPlugins> { self.push(InstrumentPlugin) } } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs index 1047167ffb..21f211bc06 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs @@ -16,6 +16,8 @@ use http::{HeaderMap, Request, Response, StatusCode, Uri}; use tower::Service; use tracing::{debug, debug_span, instrument::Instrumented, Instrument}; +use crate::shape_id::ShapeId; + use super::{MakeDebug, MakeDisplay, MakeIdentity}; pin_project_lite::pin_project! { @@ -87,15 +89,17 @@ where /// /// ``` /// # use aws_smithy_http_server::instrumentation::{sensitivity::{*, uri::*, headers::*}, *}; +/// # use aws_smithy_http_server::shape_id::ShapeId; /// # use tower::{Service, service_fn}; /// # use http::{Request, Response}; /// # async fn f(request: Request<()>) -> Result, ()> { Ok(Response::new(())) } /// # let mut svc = service_fn(f); +/// # const ID: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); /// let request_fmt = RequestFmt::new() /// .label(|index| index == 1, None) /// .query(|_| QueryMarker { key: false, value: true }); /// let response_fmt = ResponseFmt::new().status_code(); -/// let mut svc = InstrumentOperation::new(svc, "foo-operation") +/// let mut svc = InstrumentOperation::new(svc, ID) /// .request_fmt(request_fmt) /// .response_fmt(response_fmt); /// # svc.call(Request::new(())); @@ -103,17 +107,17 @@ where #[derive(Debug, Clone)] pub struct InstrumentOperation { inner: S, - operation_name: &'static str, + operation_id: ShapeId, make_request: RequestMakeFmt, make_response: ResponseMakeFmt, } impl InstrumentOperation { /// Constructs a new [`InstrumentOperation`] with no data redacted. - pub fn new(inner: S, operation_name: &'static str) -> Self { + pub fn new(inner: S, operation_id: ShapeId) -> Self { Self { inner, - operation_name, + operation_id, make_request: MakeIdentity, make_response: MakeIdentity, } @@ -127,7 +131,7 @@ impl InstrumentOperation(self, make_request: R) -> InstrumentOperation { InstrumentOperation { inner: self.inner, - operation_name: self.operation_name, + operation_id: self.operation_id, make_request, make_response: self.make_response, } @@ -139,7 +143,7 @@ impl InstrumentOperation(self, make_response: R) -> InstrumentOperation { InstrumentOperation { inner: self.inner, - operation_name: self.operation_name, + operation_id: self.operation_id, make_request: self.make_request, make_response, } @@ -170,7 +174,7 @@ where let span = { let headers = self.make_request.make_debug(request.headers()); let uri = self.make_request.make_display(request.uri()); - debug_span!("request", operation = %self.operation_name, method = %request.method(), %uri, ?headers) + debug_span!("request", operation = %self.operation_id.absolute(), method = %request.method(), %uri, ?headers) }; InstrumentedFuture { diff --git a/rust-runtime/aws-smithy-http-server/src/layer/alb_health_check.rs b/rust-runtime/aws-smithy-http-server/src/layer/alb_health_check.rs new file mode 100644 index 0000000000..8c76bf1a9f --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/layer/alb_health_check.rs @@ -0,0 +1,181 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Middleware for handling [ALB health +//! checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html). +//! +//! # Example +//! +//! ```no_run +//! use aws_smithy_http_server::layer::alb_health_check::AlbHealthCheckLayer; +//! use hyper::StatusCode; +//! use tower::Layer; +//! +//! // Handle all `/ping` health check requests by returning a `200 OK`. +//! let ping_layer = AlbHealthCheckLayer::from_handler("/ping", |_req| async { +//! StatusCode::OK +//! }); +//! # async fn handle() { } +//! let app = tower::service_fn(handle); +//! let app = ping_layer.layer(app); +//! ``` + +use std::borrow::Cow; +use std::convert::Infallible; +use std::task::{Context, Poll}; + +use futures_util::{Future, FutureExt}; +use http::StatusCode; +use hyper::{Body, Request, Response}; +use pin_project_lite::pin_project; +use tower::{service_fn, util::Oneshot, Layer, Service, ServiceExt}; + +use crate::body::BoxBody; + +use crate::plugin::either::Either; +use crate::plugin::either::EitherProj; + +/// A [`tower::Layer`] used to apply [`AlbHealthCheckService`]. +#[derive(Clone, Debug)] +pub struct AlbHealthCheckLayer { + health_check_uri: Cow<'static, str>, + health_check_handler: HealthCheckHandler, +} + +impl AlbHealthCheckLayer<()> { + /// Handle health check requests at `health_check_uri` with the specified handler. + pub fn from_handler, H: Fn(Request) -> HandlerFuture + Clone>( + health_check_uri: impl Into>, + health_check_handler: H, + ) -> AlbHealthCheckLayer< + impl Service< + Request, + Response = StatusCode, + Error = Infallible, + Future = impl Future>, + > + Clone, + > { + let service = service_fn(move |req| health_check_handler(req).map(Ok)); + + AlbHealthCheckLayer::new(health_check_uri, service) + } + + /// Handle health check requests at `health_check_uri` with the specified service. + pub fn new, Response = StatusCode>>( + health_check_uri: impl Into>, + health_check_handler: H, + ) -> AlbHealthCheckLayer { + AlbHealthCheckLayer { + health_check_uri: health_check_uri.into(), + health_check_handler, + } + } +} + +impl Layer for AlbHealthCheckLayer { + type Service = AlbHealthCheckService; + + fn layer(&self, inner: S) -> Self::Service { + AlbHealthCheckService { + inner, + layer: self.clone(), + } + } +} + +/// A middleware [`Service`] responsible for handling health check requests. +#[derive(Clone, Debug)] +pub struct AlbHealthCheckService { + inner: S, + layer: AlbHealthCheckLayer, +} + +impl Service> for AlbHealthCheckService +where + S: Service, Response = Response> + Clone, + S::Future: Send + 'static, + H: Service, Response = StatusCode, Error = Infallible> + Clone, +{ + type Response = S::Response; + type Error = S::Error; + type Future = AlbHealthCheckFuture; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + // The check that the service is ready is done by `Oneshot` below. + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request) -> Self::Future { + if req.uri() == self.layer.health_check_uri.as_ref() { + let clone = self.layer.health_check_handler.clone(); + let service = std::mem::replace(&mut self.layer.health_check_handler, clone); + let handler_future = service.oneshot(req); + + AlbHealthCheckFuture::handler_future(handler_future) + } else { + let clone = self.inner.clone(); + let service = std::mem::replace(&mut self.inner, clone); + let service_future = service.oneshot(req); + + AlbHealthCheckFuture::service_future(service_future) + } + } +} + +type HealthCheckFutureInner = Either>, Oneshot>>; + +pin_project! { + /// Future for [`AlbHealthCheckService`]. + pub struct AlbHealthCheckFuture, Response = StatusCode>, S: Service>> { + #[pin] + inner: HealthCheckFutureInner + } +} + +impl AlbHealthCheckFuture +where + H: Service, Response = StatusCode>, + S: Service>, +{ + fn handler_future(handler_future: Oneshot>) -> Self { + Self { + inner: Either::Left { value: handler_future }, + } + } + + fn service_future(service_future: Oneshot>) -> Self { + Self { + inner: Either::Right { value: service_future }, + } + } +} + +impl Future for AlbHealthCheckFuture +where + H: Service, Response = StatusCode, Error = Infallible>, + S: Service, Response = Response>, +{ + type Output = Result; + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let either_proj = self.project().inner.project(); + + match either_proj { + EitherProj::Left { value } => { + let polled: Poll = value.poll(cx).map(|res| { + res.map(|status_code| { + Response::builder() + .status(status_code) + .body(crate::body::empty()) + .unwrap() + }) + .map_err(|never| match never {}) + }); + polled + } + EitherProj::Right { value } => value.poll(cx), + } + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/layer/mod.rs b/rust-runtime/aws-smithy-http-server/src/layer/mod.rs new file mode 100644 index 0000000000..fcbce76f44 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/layer/mod.rs @@ -0,0 +1,9 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! This module hosts [`Layer`](tower::Layer)s that are generally meant to be applied _around_ the +//! [`Router`](crate::routing::Router), so they are enacted before a request is routed. + +pub mod alb_health_check; diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 6031c53e2a..a7a78733b2 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -16,10 +16,11 @@ pub mod body; pub(crate) mod error; pub mod extension; pub mod instrumentation; +pub mod layer; pub mod operation; pub mod plugin; #[doc(hidden)] -pub mod protocols; +pub mod protocol; #[doc(hidden)] pub mod rejection; pub mod request; @@ -28,6 +29,8 @@ pub mod response; pub mod routing; #[doc(hidden)] pub mod runtime_error; +pub mod service; +pub mod shape_id; #[doc(inline)] pub(crate) use self::error::Error; @@ -38,6 +41,3 @@ pub use tower_http::add_extension::{AddExtension, AddExtensionLayer}; #[cfg(test)] mod test_helpers; - -#[doc(hidden)] -pub mod proto; diff --git a/rust-runtime/aws-smithy-http-server/src/macros.rs b/rust-runtime/aws-smithy-http-server/src/macros.rs index b7df36188d..7f70ba5562 100644 --- a/rust-runtime/aws-smithy-http-server/src/macros.rs +++ b/rust-runtime/aws-smithy-http-server/src/macros.rs @@ -94,13 +94,3 @@ macro_rules! convert_to_request_rejection { } }; } - -macro_rules! convert_to_response_rejection { - ($from:ty, $to:ident) => { - impl From<$from> for ResponseRejection { - fn from(err: $from) -> Self { - Self::$to(crate::Error::new(err)) - } - } - }; -} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs index 7a30a9a12e..28ad3fd743 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs @@ -10,13 +10,10 @@ use std::{ task::{Context, Poll}, }; -use futures_util::{ - future::{Map, MapErr}, - FutureExt, TryFutureExt, -}; +use futures_util::{future::Map, FutureExt}; use tower::Service; -use super::{OperationError, OperationShape}; +use super::OperationShape; /// A utility trait used to provide an even interface for all operation handlers. /// @@ -121,8 +118,8 @@ where /// A [`Service`] provided for every [`Handler`]. pub struct IntoService { - handler: H, - _operation: PhantomData, + pub(crate) handler: H, + pub(crate) _operation: PhantomData, } impl Clone for IntoService @@ -143,14 +140,14 @@ where H: Handler, { type Response = Op::Output; - type Error = OperationError; - type Future = MapErr Self::Error>; + type Error = Op::Error; + type Future = H::Future; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future { - self.handler.call(input, exts).map_err(OperationError::Model) + self.handler.call(input, exts) } } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 3abf9e2540..8450da8366 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -24,6 +24,7 @@ //! is identified with the implementation //! //! ```rust,no_run +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # use aws_smithy_http_server::operation::OperationShape; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; @@ -31,7 +32,7 @@ //! pub struct GetShopping; //! //! impl OperationShape for GetShopping { -//! const NAME: &'static str = "GetShopping"; +//! const ID: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); //! //! type Input = CartIdentifier; //! type Output = ShoppingCart; @@ -39,11 +40,11 @@ //! } //! ``` //! -//! The behavior of a Smithy operation is encoded by an [`Operation`]. The [`OperationShape`] types can be used to -//! construct specific operations using [`OperationShapeExt::from_handler`] and [`OperationShapeExt::from_service`]. -//! The [from_handler](OperationShapeExt::from_handler) constructor takes a [`Handler`] whereas the -//! [from_service](OperationShapeExt::from_service) takes a [`OperationService`]. Both traits serve a similar purpose - -//! they provide a common interface over a class of structures. +//! The behavior of a Smithy operation is encoded by a [`Service`](tower::Service). The [`OperationShape`] types can +//! be used to construct specific operations using [`OperationShapeExt::from_handler`] and +//! [`OperationShapeExt::from_service`] methods. The [from_handler](OperationShapeExt::from_handler) constructor takes +//! a [`Handler`] whereas the [from_service](OperationShapeExt::from_service) takes a [`OperationService`]. Both traits +//! serve a similar purpose - they provide a common interface over a class of structures. //! //! ## [`Handler`] //! @@ -84,20 +85,17 @@ //! ## [`OperationService`] //! //! Similarly, the [`OperationService`] trait is implemented by all `Service<(Op::Input, ...)>` with -//! `Response = Op::Output`, and `Error = OperationError`. -//! -//! We use [`OperationError`], with a `PollError` not constrained by the model, to allow the user to provide a custom -//! [`Service::poll_ready`](tower::Service::poll_ready) implementation. +//! `Response = Op::Output`, and `Error = Op::Error`. //! //! The following are examples of [`Service`](tower::Service)s which implement [`OperationService`]: //! -//! - `Service>`. -//! - `Service<(CartIdentifier, Extension), Response = ShoppingCart, Error = OperationError>`. -//! - `Service<(CartIdentifier, Extension, Extension), Response = ShoppingCart, Error = OperationError)`. +//! - `Service`. +//! - `Service<(CartIdentifier, Extension), Response = ShoppingCart, Error = GetShoppingCartError>`. +//! - `Service<(CartIdentifier, Extension, Extension), Response = ShoppingCart, Error = GetShoppingCartError)`. //! //! Notice the parallels between [`OperationService`] and [`Handler`]. //! -//! ## Constructing an [`Operation`] +//! ## Constructing an Operation //! //! The following is an example of using both construction approaches: //! @@ -105,18 +103,19 @@ //! # use std::task::{Poll, Context}; //! # use aws_smithy_http_server::operation::*; //! # use tower::Service; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; //! # pub enum GetShoppingError {} //! # pub struct GetShopping; //! # impl OperationShape for GetShopping { -//! # const NAME: &'static str = "GetShopping"; +//! # const ID: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); //! # //! # type Input = CartIdentifier; //! # type Output = ShoppingCart; //! # type Error = GetShoppingError; //! # } -//! # type OpFuture = std::future::Ready>>; +//! # type OpFuture = std::future::Ready>; //! // Construction of an `Operation` from a `Handler`. //! //! async fn op_handler(input: CartIdentifier) -> Result { @@ -127,22 +126,18 @@ //! //! // Construction of an `Operation` from a `Service`. //! -//! pub struct PollError; -//! //! pub struct OpService; //! //! impl Service for OpService { //! type Response = ShoppingCart; -//! type Error = OperationError; +//! type Error = GetShoppingError; //! type Future = OpFuture; //! //! fn poll_ready(&mut self, cx: &mut Context) -> Poll> { -//! // NOTE: This MUST NOT return `Err(OperationError::Model(_))`. //! todo!() //! } //! //! fn call(&mut self, request: CartIdentifier) -> Self::Future { -//! // NOTE: This MUST NOT return `Err(OperationError::Poll(_))`. //! todo!() //! } //! } @@ -153,25 +148,15 @@ //! //! ## Upgrading Smithy services to HTTP services //! -//! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. After an [`Operation`] is -//! constructed they are converted to a canonical form -//! `Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError>`. The -//! [`UpgradeLayer`] acts upon such services by converting them to -//! `Service`. +//! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. They are converted to a +//! canonical form `Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error>`. The +//! [`UpgradePlugin`] acts upon such services by converting them to +//! `Service` by applying serialization/deserialization +//! and validation specified by the Smithy contract. //! -//! Note that the `PollError` is still exposed, for two reasons: //! -//! - Smithy is agnostic to `PollError` and therefore we have no prescribed way to serialize it to a [`http::Response`] -//! , unlike the operation errors. -//! - The intention of `PollError` is to signal that the underlying service is no longer able to take requests, so -//! should be discarded. See [`Service::poll_ready`](tower::Service::poll_ready). -//! -//! The [`UpgradeLayer`] and it's [`Layer::Service`](tower::Layer::Service) [`Upgrade`] are both parameterized by a -//! protocol. This allows for upgrading to `Service` to be -//! protocol dependent. -//! -//! The [`Operation::upgrade`] will apply [`UpgradeLayer`] to `S` then apply the [`Layer`](tower::Layer) `L`. The -//! service builder provided to the user will perform this composition on `build`. +//! The [`UpgradePlugin`], being a [`Plugin`](crate::plugin::Plugin), is parameterized by a protocol. This allows for +//! upgrading to `Service` to be protocol dependent. //! //! [Smithy operation]: https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation @@ -180,67 +165,7 @@ mod operation_service; mod shape; mod upgrade; -use tower::layer::util::{Identity, Stack}; - pub use handler::*; pub use operation_service::*; pub use shape::*; pub use upgrade::*; - -/// A Smithy operation, represented by a [`Service`](tower::Service) `S` and a [`Layer`](tower::Layer) `L`. -/// -/// The `L` is held and applied lazily during [`Upgradable::upgrade`]. -pub struct Operation { - /// The inner [`Service`](tower::Service) representing the logic of the operation. - pub inner: S, - /// The [`Layer`](tower::Layer) applied to the HTTP [`Service`](tower::Service) after `S` has been wrapped in - /// [`Upgrade`]. - pub layer: L, -} - -impl Operation { - /// Applies a [`Layer`](tower::Layer) to the operation _after_ it has been upgraded via [`Operation::upgrade`]. - pub fn layer(self, layer: NewL) -> Operation> { - Operation { - inner: self.inner, - layer: Stack::new(self.layer, layer), - } - } -} - -impl Operation> { - /// Creates an [`Operation`] from a [`Service`](tower::Service). - pub fn from_service(inner: S) -> Self - where - Op: OperationShape, - S: OperationService, - { - Self { - inner: inner.canonicalize(), - layer: Identity::new(), - } - } -} - -impl Operation> { - /// Creates an [`Operation`] from a [`Handler`]. - pub fn from_handler(handler: H) -> Self - where - Op: OperationShape, - H: Handler, - { - Self { - inner: handler.into_service(), - layer: Identity::new(), - } - } -} - -/// The operation [`Service`](tower::Service) has two classes of failure modes - those specified by the Smithy model -/// and those associated with [`Service::poll_ready`](tower::Service::poll_ready). -pub enum OperationError { - /// An error modelled by the Smithy model occurred. - Model(ModelError), - /// A [`Service::poll_ready`](tower::Service::poll_ready) failure occurred. - PollReady(PollError), -} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs index 2dd7f6d92e..b9a87aa6e9 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs @@ -10,7 +10,7 @@ use std::{ use tower::Service; -use super::{OperationError, OperationShape}; +use super::OperationShape; /// A utility trait used to provide an even interface for all operation services. /// @@ -19,8 +19,7 @@ use super::{OperationError, OperationShape}; /// [`IntoService`](super::IntoService). /// /// See [`operation`](crate::operation) documentation for more info. -pub trait OperationService: - Service> +pub trait OperationService: Service where Op: OperationShape, { @@ -31,10 +30,10 @@ where } // `Service` -impl OperationService for S +impl OperationService for S where Op: OperationShape, - S: Service>, + S: Service, { type Normalized = Op::Input; @@ -44,10 +43,10 @@ where } // `Service<(Op::Input, Ext0)>` -impl OperationService for S +impl OperationService for S where Op: OperationShape, - S: Service<(Op::Input, Ext0), Response = Op::Output, Error = OperationError>, + S: Service<(Op::Input, Ext0), Response = Op::Output, Error = Op::Error>, { type Normalized = (Op::Input, Ext0); @@ -57,10 +56,10 @@ where } // `Service<(Op::Input, Ext0, Ext1)>` -impl OperationService for S +impl OperationService for S where Op: OperationShape, - S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = OperationError>, + S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = Op::Error>, { type Normalized = (Op::Input, Ext0, Ext1); @@ -70,39 +69,37 @@ where } /// An extension trait of [`OperationService`]. -pub trait OperationServiceExt: OperationService +pub trait OperationServiceExt: OperationService where Op: OperationShape, { /// Convert the [`OperationService`] into a canonicalized [`Service`]. - fn canonicalize(self) -> Normalize + fn normalize(self) -> Normalize where Self: Sized, { Normalize { inner: self, _operation: PhantomData, - _poll_error: PhantomData, } } } -impl OperationServiceExt for F +impl OperationServiceExt for F where Op: OperationShape, - F: OperationService, + F: OperationService, { } /// A [`Service`] normalizing the request type of a [`OperationService`]. #[derive(Debug)] -pub struct Normalize { - inner: S, - _operation: PhantomData, - _poll_error: PhantomData, +pub struct Normalize { + pub(crate) inner: S, + pub(crate) _operation: PhantomData, } -impl Clone for Normalize +impl Clone for Normalize where S: Clone, { @@ -110,15 +107,14 @@ where Self { inner: self.inner.clone(), _operation: PhantomData, - _poll_error: PhantomData, } } } -impl Service<(Op::Input, Exts)> for Normalize +impl Service<(Op::Input, Exts)> for Normalize where Op: OperationShape, - S: OperationService, + S: OperationService, { type Response = S::Response; type Error = S::Error; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs index 9990326279..6d3d774868 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs @@ -3,14 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::{Handler, IntoService, Normalize, Operation, OperationService}; +use std::marker::PhantomData; + +use super::{Handler, IntoService, Normalize, OperationService}; +use crate::shape_id::ShapeId; /// Models the [Smithy Operation shape]. /// /// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation pub trait OperationShape { - /// The name of the operation. - const NAME: &'static str; + /// The ID of the operation. + const ID: ShapeId; /// The operation input. type Input; @@ -23,22 +26,29 @@ pub trait OperationShape { /// An extension trait over [`OperationShape`]. pub trait OperationShapeExt: OperationShape { - /// Creates a new [`Operation`] for well-formed [`Handler`]s. - fn from_handler(handler: H) -> Operation> + /// Creates a new [`Service`](tower::Service), [`IntoService`], for well-formed [`Handler`]s. + fn from_handler(handler: H) -> IntoService where H: Handler, Self: Sized, { - Operation::from_handler(handler) + IntoService { + handler, + _operation: PhantomData, + } } - /// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s. - fn from_service(svc: S) -> Operation> + /// Creates a new normalized [`Service`](tower::Service), [`Normalize`], for well-formed + /// [`Service`](tower::Service)s. + fn from_service(svc: S) -> Normalize where - S: OperationService, + S: OperationService, Self: Sized, { - Operation::from_service(svc) + Normalize { + inner: svc, + _operation: PhantomData, + } } } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs index 42c4a5b26b..15e87ebc19 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -13,56 +13,51 @@ use std::{ use futures_util::ready; use pin_project_lite::pin_project; -use tower::{layer::util::Stack, Layer, Service}; +use tower::{util::Oneshot, Service, ServiceExt}; use tracing::error; use crate::{ - body::BoxBody, - plugin::Plugin, - request::{FromParts, FromRequest}, - response::IntoResponse, - routing::Route, - runtime_error::InternalFailureException, + body::BoxBody, plugin::Plugin, request::FromRequest, response::IntoResponse, + runtime_error::InternalFailureException, service::ServiceShape, }; -use super::{Operation, OperationError, OperationShape}; +use super::OperationShape; -/// A [`Layer`] responsible for taking an operation [`Service`], accepting and returning Smithy +/// A [`Plugin`] responsible for taking an operation [`Service`], accepting and returning Smithy /// types and converting it into a [`Service`] taking and returning [`http`] types. /// /// See [`Upgrade`]. #[derive(Debug, Clone)] -pub struct UpgradeLayer { - _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, +pub struct UpgradePlugin { + _extractors: PhantomData, } -impl Default for UpgradeLayer { +impl Default for UpgradePlugin { fn default() -> Self { Self { - _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, + _extractors: PhantomData, } } } -impl UpgradeLayer { - /// Creates a new [`UpgradeLayer`]. +impl UpgradePlugin { + /// Creates a new [`UpgradePlugin`]. pub fn new() -> Self { Self::default() } } -impl Layer for UpgradeLayer { - type Service = Upgrade; +impl Plugin for UpgradePlugin +where + Ser: ServiceShape, + Op: OperationShape, +{ + type Output = Upgrade; - fn layer(&self, inner: S) -> Self::Service { + fn apply(&self, inner: T) -> Self::Output { Upgrade { _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, + _input: PhantomData, inner, } } @@ -70,22 +65,20 @@ impl Layer for UpgradeLayer { /// A [`Service`] responsible for wrapping an operation [`Service`] accepting and returning Smithy /// types, and converting it into a [`Service`] accepting and returning [`http`] types. -pub struct Upgrade { +pub struct Upgrade { _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, + _input: PhantomData, inner: S, } -impl Clone for Upgrade +impl Clone for Upgrade where S: Clone, { fn clone(&self) -> Self { Self { _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, + _input: PhantomData, inner: self.inner.clone(), } } @@ -106,40 +99,29 @@ pin_project! { } } -type InnerAlias = Inner<<(Input, Exts) as FromRequest>::Future, Fut>; +type InnerAlias = Inner<>::Future, Oneshot>; pin_project! { /// The [`Service::Future`] of [`Upgrade`]. - pub struct UpgradeFuture + pub struct UpgradeFuture where - Operation: OperationShape, - (Operation::Input, Exts): FromRequest, - S: Service<(Operation::Input, Exts)>, + Input: FromRequest, + S: Service, { - service: S, + service: Option, #[pin] - inner: InnerAlias + inner: InnerAlias } } -impl Future for UpgradeFuture +impl Future for UpgradeFuture where - // `Op` is used to specify the operation shape - Op: OperationShape, - // Smithy input must convert from a HTTP request - Op::Input: FromRequest, - // Smithy output must convert into a HTTP response - Op::Output: IntoResponse

, - // Smithy error must convert into a HTTP response - OpError: IntoResponse

, - - // Must be able to convert extensions - Exts: FromParts

, - - // The signature of the inner service is correct - S: Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError>, + Input: FromRequest, + S: Service, + S::Response: IntoResponse

, + S::Error: IntoResponse

, { - type Output = Result, PollError>; + type Output = Result, Infallible>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { @@ -150,7 +132,11 @@ where InnerProj::FromRequest { inner } => { let result = ready!(inner.poll(cx)); match result { - Ok(ok) => this.service.call(ok), + Ok(ok) => this + .service + .take() + .expect("futures cannot be polled after completion") + .oneshot(ok), Err(err) => return Poll::Ready(Ok(err.into_response())), } } @@ -158,10 +144,7 @@ where let result = ready!(call.poll(cx)); let output = match result { Ok(ok) => ok.into_response(), - Err(OperationError::Model(err)) => err.into_response(), - Err(OperationError::PollReady(_)) => { - unreachable!("poll error should not be raised") - } + Err(err) => err.into_response(), }; return Poll::Ready(Ok(output)); } @@ -172,119 +155,45 @@ where } } -impl Service> for Upgrade +impl Service> for Upgrade where - // `Op` is used to specify the operation shape - Op: OperationShape, - // Smithy input must convert from a HTTP request - Op::Input: FromRequest, - // Smithy output must convert into a HTTP response - Op::Output: IntoResponse

, - // Smithy error must convert into a HTTP response - OpError: IntoResponse

, - - // Must be able to convert extensions - Exts: FromParts

, - - // The signature of the inner service is correct - S: Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError> + Clone, + Input: FromRequest, + S: Service + Clone, + S::Response: IntoResponse

, + S::Error: IntoResponse

, { type Response = http::Response; - type Error = PollError; - type Future = UpgradeFuture; + type Error = Infallible; + type Future = UpgradeFuture; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx).map_err(|err| match err { - OperationError::PollReady(err) => err, - OperationError::Model(_) => unreachable!("operation error should not be raised"), - }) + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { let clone = self.inner.clone(); let service = std::mem::replace(&mut self.inner, clone); UpgradeFuture { - service, + service: Some(service), inner: Inner::FromRequest { - inner: <(Op::Input, Exts) as FromRequest>::from_request(req), + inner: >::from_request(req), }, } } } -/// An interface to convert a representation of a Smithy operation into a [`Route`]. -/// -/// See the [module](crate::operation) documentation for more information. -pub trait Upgradable { - /// Upgrade the representation of a Smithy operation to a [`Route`]. - fn upgrade(self, plugin: &Plugin) -> Route; -} - -type UpgradedService = - <>::Layer as Layer>::Service>>>::Service; - -impl Upgradable for Operation -where - // `Op` is used to specify the operation shape - Op: OperationShape, - - // Smithy input must convert from a HTTP request - Op::Input: FromRequest, - // Smithy output must convert into a HTTP response - Op::Output: IntoResponse

, - // Smithy error must convert into a HTTP response - Op::Error: IntoResponse

, - - // Must be able to convert extensions - Exts: FromParts

, - - // The signature of the inner service is correct - Pl::Service: - Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError> + Clone, - - // The plugin takes this operation as input - Pl: Plugin, - - // The modified Layer applies correctly to `Upgrade` - Pl::Layer: Layer>, - - // For `Route::new` for the resulting service - UpgradedService: - Service, Response = http::Response, Error = Infallible> + Clone + Send + 'static, - as Service>>::Future: Send + 'static, -{ - /// Takes the [`Operation`](Operation), applies [`Plugin`], then applies [`UpgradeLayer`] to - /// the modified `S`, then finally applies the modified `L`. - /// - /// The composition is made explicit in the method constraints and return type. - fn upgrade(self, plugin: &Pl) -> Route { - let mapped = plugin.map(self); - let layer = Stack::new(UpgradeLayer::new(), mapped.layer); - Route::new(layer.layer(mapped.inner)) - } -} - -/// A marker struct indicating an [`Operation`] has not been set in a builder. -/// -/// This _does_ implement [`Upgradable`] but produces a [`Service`] which always returns an internal failure message. -pub struct FailOnMissingOperation; - -impl Upgradable for FailOnMissingOperation -where - InternalFailureException: IntoResponse

, - P: 'static, -{ - fn upgrade(self, _plugin: &Pl) -> Route { - Route::new(MissingFailure { _protocol: PhantomData }) - } -} - /// A [`Service`] which always returns an internal failure message and logs an error. #[derive(Copy)] pub struct MissingFailure

{ _protocol: PhantomData, } +impl

Default for MissingFailure

{ + fn default() -> Self { + Self { _protocol: PhantomData } + } +} + impl

Clone for MissingFailure

{ fn clone(&self) -> Self { MissingFailure { _protocol: PhantomData } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs index 75685c97b9..d78ba4c937 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs @@ -3,59 +3,69 @@ * SPDX-License-Identifier: Apache-2.0 */ -use tower::layer::util::Stack; - -use crate::operation::{Operation, OperationShape}; +use crate::service::ContainsOperation; use super::Plugin; -/// An adapter to convert a `Fn(&'static str) -> Layer` closure into a [`Plugin`]. See [`plugin_from_operation_name_fn`] for more details. -pub struct OperationNameFn { +/// An adapter to convert a `Fn(ShapeId, T) -> Service` closure into a [`Plugin`]. See [`plugin_from_operation_fn`] for more details. +pub struct OperationFn { f: F, } -impl Plugin for OperationNameFn +impl Plugin for OperationFn where - F: Fn(&'static str) -> NewLayer, - Op: OperationShape, + Ser: ContainsOperation, + F: Fn(Ser::Operations, T) -> NewService, { - type Service = S; - type Layer = Stack; + type Output = NewService; - fn map(&self, input: Operation) -> Operation { - input.layer((self.f)(Op::NAME)) + fn apply(&self, input: T) -> Self::Output { + (self.f)(Ser::VALUE, input) } } -/// Constructs a [`Plugin`] using a closure over the operation name `F: Fn(&'static str) -> L` where `L` is a HTTP -/// [`Layer`](tower::Layer). +/// Constructs a [`Plugin`] using a closure over the [`ServiceShape::] `F: Fn(ShapeId, T) -> Service`. /// /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::plugin_from_operation_name_fn; +/// # use aws_smithy_http_server::{service::*, operation::OperationShape, plugin::Plugin, shape_id::ShapeId}; +/// # pub enum Operation { CheckHealth, GetPokemonSpecies } +/// # impl Operation { fn shape_id(&self) -> ShapeId { ShapeId::new("", "", "") }} +/// # pub struct CheckHealth; +/// # pub struct GetPokemonSpecies; +/// # pub struct PokemonService; +/// # impl ServiceShape for PokemonService { +/// # const ID: ShapeId = ShapeId::new("", "", ""); +/// # const VERSION: Option<&'static str> = None; +/// # type Protocol = (); +/// # type Operations = Operation; +/// # } +/// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +/// # impl OperationShape for GetPokemonSpecies { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +/// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } +/// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::GetPokemonSpecies; } +/// use aws_smithy_http_server::plugin::plugin_from_operation_fn; /// use tower::layer::layer_fn; /// -/// // A `Service` which prints the operation name before calling `S`. -/// struct PrintService { -/// operation_name: &'static str, +/// struct FooService { +/// info: String, /// inner: S /// } /// -/// // A `Layer` applying `PrintService`. -/// struct PrintLayer { -/// operation_name: &'static str +/// fn map(op: Operation, inner: S) -> FooService { +/// match op { +/// Operation::CheckHealth => FooService { info: op.shape_id().name().to_string(), inner }, +/// Operation::GetPokemonSpecies => FooService { info: "bar".to_string(), inner }, +/// _ => todo!() +/// } /// } /// -/// // Defines a closure taking the operation name to `PrintLayer`. -/// let f = |operation_name| PrintLayer { operation_name }; -/// -/// // This plugin applies the `PrintService` middleware around every operation. -/// let plugin = plugin_from_operation_name_fn(f); +/// // This plugin applies the `FooService` middleware around every operation. +/// let plugin = plugin_from_operation_fn(map); +/// # let _ = Plugin::::apply(&plugin, ()); +/// # let _ = Plugin::::apply(&plugin, ()); /// ``` -pub fn plugin_from_operation_name_fn(f: F) -> OperationNameFn -where - F: Fn(&'static str) -> L, -{ - OperationNameFn { f } +pub fn plugin_from_operation_fn(f: F) -> OperationFn { + OperationFn { f } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs index bf33546ca1..d0b6192ba0 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs @@ -13,18 +13,19 @@ use std::{ }; use tower::{Layer, Service}; -use crate::operation::Operation; - use super::Plugin; +// TODO(https://github.com/awslabs/smithy-rs/pull/2441#pullrequestreview-1331345692): Seems like +// this type should land in `tower-0.5`. pin_project! { - /// Combine two different [`Future`]/[`Service`]/[`Layer`]/[`Plugin`] types into a single type. + /// Combine two different [`Futures`](std::future::Future)/[`Services`](tower::Service)/ + /// [`Layers`](tower::Layer)/[`Plugins`](super::Plugin) into a single type. /// - /// # Notes on [`Future`] + /// # Notes on [`Future`](std::future::Future) /// /// The [`Future::Output`] must be identical. /// - /// # Notes on [`Service`] + /// # Notes on [`Service`](tower::Service) /// /// The [`Service::Response`] and [`Service::Error`] must be identical. #[derive(Clone, Debug)] @@ -103,30 +104,21 @@ where } } -impl Plugin for Either +impl Plugin for Either where - Le: Plugin, - Ri: Plugin, + Le: Plugin, + Ri: Plugin, { - type Service = Either; - type Layer = Either; + type Output = Either; - fn map(&self, input: Operation) -> Operation { + fn apply(&self, input: T) -> Self::Output { match self { - Either::Left { value } => { - let Operation { inner, layer } = value.map(input); - Operation { - inner: Either::Left { value: inner }, - layer: Either::Left { value: layer }, - } - } - Either::Right { value } => { - let Operation { inner, layer } = value.map(input); - Operation { - inner: Either::Right { value: inner }, - layer: Either::Right { value: layer }, - } - } + Either::Left { value } => Either::Left { + value: value.apply(input), + }, + Either::Right { value } => Either::Right { + value: value.apply(input), + }, } } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index 814398b089..f29fac8e50 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -3,68 +3,76 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::{either::Either, IdentityPlugin}; +use super::{either::Either, IdentityPlugin, ModelMarker}; -use crate::operation::{Operation, OperationShape}; +use crate::operation::OperationShape; +use crate::service::ContainsOperation; -use super::Plugin; +use super::{HttpMarker, Plugin}; /// Filters the application of an inner [`Plugin`] using a predicate over the -/// [`OperationShape::NAME`](crate::operation::OperationShape). +/// [`ServiceShape::Operations`](crate::service::ServiceShape::Operations). /// -/// See [`filter_by_operation_name`] for more details. -pub struct FilterByOperationName { +/// This contrasts with [`Scoped`](crate::plugin::Scoped) which can be used to selectively apply a [`Plugin`] to a +/// subset of operations at _compile time_. +/// +/// See [`filter_by_operation`] for more details. +pub struct FilterByOperation { inner: Inner, predicate: F, } +impl Plugin for FilterByOperation +where + Ser: ContainsOperation, + F: Fn(Ser::Operations) -> bool, + Inner: Plugin, + Op: OperationShape, +{ + type Output = Either; + + fn apply(&self, input: T) -> Self::Output { + let either_plugin = if (self.predicate)(>::VALUE) { + Either::Left { value: &self.inner } + } else { + Either::Right { value: IdentityPlugin } + }; + either_plugin.apply(input) + } +} + +impl HttpMarker for FilterByOperation where Inner: HttpMarker {} +impl ModelMarker for FilterByOperation where Inner: ModelMarker {} + /// Filters the application of an inner [`Plugin`] using a predicate over the -/// [`OperationShape::NAME`](crate::operation::OperationShape). +/// [`ServiceShape::Operations`](crate::service::ServiceShape::Operations). +/// +/// Users should prefer [`Scoped`](crate::plugin::Scoped) and fallback to [`filter_by_operation`] +/// in cases where [`Plugin`] application must be decided at runtime. /// /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::filter_by_operation_name; -/// # use aws_smithy_http_server::{plugin::Plugin, operation::{Operation, OperationShape}}; +/// use aws_smithy_http_server::plugin::filter_by_operation; +/// # use aws_smithy_http_server::{plugin::Plugin, operation::OperationShape, shape_id::ShapeId, service::{ServiceShape, ContainsOperation}}; /// # struct Pl; +/// # struct PokemonService; +/// # #[derive(PartialEq, Eq)] +/// # enum Operation { CheckHealth } +/// # impl ServiceShape for PokemonService { const VERSION: Option<&'static str> = None; const ID: ShapeId = ShapeId::new("", "", ""); type Operations = Operation; type Protocol = (); } +/// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } /// # struct CheckHealth; -/// # impl OperationShape for CheckHealth { const NAME: &'static str = ""; type Input = (); type Output = (); type Error = (); } -/// # impl Plugin<(), CheckHealth, (), ()> for Pl { type Service = (); type Layer = (); fn map(&self, input: Operation<(), ()>) -> Operation<(), ()> { input }} +/// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +/// # impl Plugin for Pl { type Output = (); fn apply(&self, input: ()) -> Self::Output { input }} /// # let plugin = Pl; -/// # let operation = Operation { inner: (), layer: () }; +/// # let svc = (); /// // Prevents `plugin` from being applied to the `CheckHealth` operation. -/// let filtered_plugin = filter_by_operation_name(plugin, |name| name != CheckHealth::NAME); -/// let new_operation = filtered_plugin.map(operation); +/// let filtered_plugin = filter_by_operation(plugin, |name| name != Operation::CheckHealth); +/// let new_operation = filtered_plugin.apply(svc); /// ``` -pub fn filter_by_operation_name(plugins: Inner, predicate: F) -> FilterByOperationName -where - F: Fn(&str) -> bool, -{ - FilterByOperationName::new(plugins, predicate) -} - -impl FilterByOperationName { - /// Creates a new [`FilterByOperationName`]. - fn new(inner: Inner, predicate: F) -> Self { - Self { inner, predicate } - } -} - -impl Plugin for FilterByOperationName -where - F: Fn(&str) -> bool, - Inner: Plugin, - Op: OperationShape, -{ - type Service = Either; - type Layer = Either; - - fn map(&self, input: Operation) -> Operation { - let either_plugin = if (self.predicate)(Op::NAME) { - Either::Left { value: &self.inner } - } else { - Either::Right { value: IdentityPlugin } - }; - either_plugin.map(input) +pub fn filter_by_operation(plugins: Inner, predicate: F) -> FilterByOperation { + FilterByOperation { + inner: plugins, + predicate, } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/http_plugins.rs b/rust-runtime/aws-smithy-http-server/src/plugin/http_plugins.rs new file mode 100644 index 0000000000..63aece88a6 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/plugin/http_plugins.rs @@ -0,0 +1,203 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// If you make any updates to this file (including Rust docs), make sure you make them to +// `model_plugins.rs` too! + +use crate::plugin::{IdentityPlugin, Plugin, PluginStack}; + +use super::{HttpMarker, LayerPlugin}; + +/// A wrapper struct for composing HTTP plugins. +/// It can be used as input for the `builder_with_plugins` method on the generated service struct +/// (e.g. `PokemonService::builder_with_plugins`). +/// +/// ## Applying plugins in a sequence +/// +/// You can use the [`push`](HttpPlugins::push) method to apply a new HTTP plugin after the ones that +/// have already been registered. +/// +/// ```rust +/// use aws_smithy_http_server::plugin::HttpPlugins; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; +/// +/// let http_plugins = HttpPlugins::new().push(LoggingPlugin).push(MetricsPlugin); +/// ``` +/// +/// The plugins' runtime logic is executed in registration order. +/// In our example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. +/// +/// ## Wrapping the current plugin pipeline +/// +/// From time to time, you might have a need to transform the entire pipeline that has been built +/// so far - e.g. you only want to apply those plugins for a specific operation. +/// +/// `HttpPlugins` is itself a [`Plugin`]: you can apply any transformation that expects a +/// [`Plugin`] to an entire pipeline. In this case, we could use a [scoped +/// plugin](crate::plugin::Scoped) to limit the scope of the logging and metrics plugins to the +/// `CheckHealth` operation: +/// +/// ```rust +/// use aws_smithy_http_server::scope; +/// use aws_smithy_http_server::plugin::{HttpPlugins, Scoped}; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; +/// use aws_smithy_http_server::shape_id::ShapeId; +/// # #[derive(PartialEq)] +/// # enum Operation { CheckHealth } +/// # struct CheckHealth; +/// # impl CheckHealth { const ID: ShapeId = ShapeId::new("namespace#MyName", "namespace", "MyName"); } +/// +/// // The logging and metrics plugins will only be applied to the `CheckHealth` operation. +/// let plugin = HttpPlugins::new() +/// .push(LoggingPlugin) +/// .push(MetricsPlugin); +/// +/// scope! { +/// struct OnlyCheckHealth { +/// includes: [CheckHealth], +/// excludes: [/* The rest of the operations go here */] +/// } +/// } +/// +/// let filtered_plugin = Scoped::new::(&plugin); +/// let http_plugins = HttpPlugins::new() +/// .push(filtered_plugin) +/// // The auth plugin will be applied to all operations. +/// .push(AuthPlugin); +/// ``` +/// +/// ## Concatenating two collections of HTTP plugins +/// +/// `HttpPlugins` is a good way to bundle together multiple plugins, ensuring they are all +/// registered in the correct order. +/// +/// Since `HttpPlugins` is itself a HTTP plugin (it implements the `HttpMarker` trait), you can use +/// the [`push`](HttpPlugins::push) to append, at once, all the HTTP plugins in another +/// `HttpPlugins` to the current `HttpPlugins`: +/// +/// ```rust +/// use aws_smithy_http_server::plugin::{IdentityPlugin, HttpPlugins, PluginStack}; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; +/// +/// pub fn get_bundled_http_plugins() -> HttpPlugins>> { +/// HttpPlugins::new().push(LoggingPlugin).push(MetricsPlugin) +/// } +/// +/// let http_plugins = HttpPlugins::new() +/// .push(AuthPlugin) +/// .push(get_bundled_http_plugins()); +/// ``` +/// +/// ## Providing custom methods on `HttpPlugins` +/// +/// You use an **extension trait** to add custom methods on `HttpPlugins`. +/// +/// This is a simple example using `AuthPlugin`: +/// +/// ```rust +/// use aws_smithy_http_server::plugin::{HttpPlugins, PluginStack}; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; +/// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; +/// +/// pub trait AuthPluginExt { +/// fn with_auth(self) -> HttpPlugins>; +/// } +/// +/// impl AuthPluginExt for HttpPlugins { +/// fn with_auth(self) -> HttpPlugins> { +/// self.push(AuthPlugin) +/// } +/// } +/// +/// let http_plugins = HttpPlugins::new() +/// .push(LoggingPlugin) +/// // Our custom method! +/// .with_auth(); +/// ``` +#[derive(Debug)] +pub struct HttpPlugins

(pub(crate) P); + +impl Default for HttpPlugins { + fn default() -> Self { + Self(IdentityPlugin) + } +} + +impl HttpPlugins { + /// Create an empty [`HttpPlugins`]. + /// + /// You can use [`HttpPlugins::push`] to add plugins to it. + pub fn new() -> Self { + Self::default() + } +} + +impl

HttpPlugins

{ + /// Apply a new HTTP plugin after the ones that have already been registered. + /// + /// ```rust + /// use aws_smithy_http_server::plugin::HttpPlugins; + /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; + /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; + /// + /// let http_plugins = HttpPlugins::new().push(LoggingPlugin).push(MetricsPlugin); + /// ``` + /// + /// The plugins' runtime logic is executed in registration order. + /// In our example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. + /// + /// ## Implementation notes + /// + /// Plugins are applied to the underlying [`Service`](tower::Service) in opposite order compared + /// to their registration order. + /// + /// As an example: + /// + /// ```rust,compile_fail + /// #[derive(Debug)] + /// pub struct PrintPlugin; + /// + /// impl Plugin for PrintPlugin + /// // [...] + /// { + /// // [...] + /// fn apply(&self, inner: T) -> Self::Service { + /// PrintService { + /// inner, + /// service_id: Ser::ID, + /// operation_id: Op::ID + /// } + /// } + /// } + /// ``` + // We eagerly require `NewPlugin: HttpMarker`, despite not really needing it, because compiler + // errors get _substantially_ better if the user makes a mistake. + pub fn push(self, new_plugin: NewPlugin) -> HttpPlugins> { + HttpPlugins(PluginStack::new(new_plugin, self.0)) + } + + /// Applies a single [`tower::Layer`] to all operations _before_ they are deserialized. + pub fn layer(self, layer: L) -> HttpPlugins, P>> { + HttpPlugins(PluginStack::new(LayerPlugin(layer), self.0)) + } +} + +impl Plugin for HttpPlugins +where + InnerPlugin: Plugin, +{ + type Output = InnerPlugin::Output; + + fn apply(&self, input: T) -> Self::Output { + self.0.apply(input) + } +} + +impl HttpMarker for HttpPlugins where InnerPlugin: HttpMarker {} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs b/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs index d0a6d1390b..6ec684a532 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs @@ -3,18 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::operation::Operation; +use super::{HttpMarker, ModelMarker, Plugin}; -use super::Plugin; - -/// A [`Plugin`] that maps an `input` [`Operation`] to itself. +/// A [`Plugin`] that maps a service to itself. +#[derive(Debug)] pub struct IdentityPlugin; -impl Plugin for IdentityPlugin { - type Service = S; - type Layer = L; +impl Plugin for IdentityPlugin { + type Output = S; - fn map(&self, input: Operation) -> Operation { - input + fn apply(&self, svc: S) -> S { + svc } } + +impl ModelMarker for IdentityPlugin {} +impl HttpMarker for IdentityPlugin {} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs b/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs index d7a055f291..0a2f31bc48 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs @@ -3,23 +3,56 @@ * SPDX-License-Identifier: Apache-2.0 */ -use tower::layer::util::Stack; +use std::marker::PhantomData; -use crate::operation::Operation; +use tower::Layer; -use super::Plugin; +use super::{HttpMarker, ModelMarker, Plugin}; -/// A [`Plugin`] which appends a HTTP [`Layer`](tower::Layer) `L` to the existing `Layer` in [`Operation`](Operation). -pub struct HttpLayer(pub L); +/// A [`Plugin`] which acts as a [`Layer`] `L`. +pub struct LayerPlugin(pub L); -impl Plugin for HttpLayer +impl Plugin for LayerPlugin where - NewLayer: Clone, + L: Layer, { - type Service = S; - type Layer = Stack; + type Output = L::Service; - fn map(&self, input: Operation) -> Operation { - input.layer(self.0.clone()) + fn apply(&self, svc: S) -> Self::Output { + self.0.layer(svc) + } +} + +// Without more information about what the layer `L` does, we can't know whether it's appropriate +// to run this plugin as a HTTP plugin or a model plugin, so we implement both marker traits. + +impl HttpMarker for LayerPlugin {} +impl ModelMarker for LayerPlugin {} + +/// A [`Layer`] which acts as a [`Plugin`] `Pl` for specific protocol `P` and operation `Op`. +pub struct PluginLayer { + plugin: Pl, + _ser: PhantomData, + _op: PhantomData, +} + +impl Layer for PluginLayer +where + Pl: Plugin, +{ + type Service = Pl::Output; + + fn layer(&self, inner: S) -> Self::Service { + self.plugin.apply(inner) + } +} + +impl PluginLayer<(), (), Pl> { + pub fn new(plugin: Pl) -> PluginLayer { + PluginLayer { + plugin, + _ser: PhantomData, + _op: PhantomData, + } } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index 72db50cbb4..a5d5ec66ec 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -5,56 +5,138 @@ //! The plugin system allows you to build middleware with an awareness of the operation it is applied to. //! -//! The system centers around the [`Plugin`] trait. In addition, this module provides helpers for composing and -//! combining [`Plugin`]s. +//! The system centers around the [`Plugin`], [`HttpMarker`], and [`ModelMarker`] traits. In +//! addition, this module provides helpers for composing and combining [`Plugin`]s. +//! +//! # HTTP plugins vs model plugins +//! +//! Plugins come in two flavors: _HTTP_ plugins and _model_ plugins. The key difference between +//! them is _when_ they run: +//! +//! - A HTTP plugin acts on the HTTP request before it is deserialized, and acts on the HTTP response +//! after it is serialized. +//! - A model plugin acts on the modeled operation input after it is deserialized, and acts on the +//! modeled operation output or the modeled operation error before it is serialized. +//! +//! See the relevant section in [the book], which contains an illustrative diagram. +//! +//! Both kinds of plugins implement the [`Plugin`] trait, but only HTTP plugins implement the +//! [`HttpMarker`] trait and only model plugins implement the [`ModelMarker`] trait. There is no +//! difference in how an HTTP plugin or a model plugin is applied, so both the [`HttpMarker`] trait +//! and the [`ModelMarker`] trait are _marker traits_, they carry no behavior. Their only purpose +//! is to mark a plugin, at the type system leve, as allowed to run at a certain time. A plugin can be +//! _both_ a HTTP plugin and a model plugin by implementing both traits; in this case, when the +//! plugin runs is decided by you when you register it in your application. [`IdentityPlugin`], +//! [`Scoped`], and [`LayerPlugin`] are examples of plugins that implement both traits. +//! +//! In practice, most plugins are HTTP plugins. Since HTTP plugins run before a request has been +//! correctly deserialized, HTTP plugins should be fast and lightweight. Only use model plugins if +//! you absolutely require your middleware to run after deserialization, or to act on particular +//! fields of your deserialized operation's input/output/errors. +//! +//! [the book]: https://awslabs.github.io/smithy-rs/design/server/anatomy.html //! //! # Filtered application of a HTTP [`Layer`](tower::Layer) //! //! ``` //! # use aws_smithy_http_server::plugin::*; +//! # use aws_smithy_http_server::scope; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # let layer = (); +//! # #[derive(PartialEq)] +//! # enum Operation { GetPokemonSpecies } //! # struct GetPokemonSpecies; -//! # impl GetPokemonSpecies { const NAME: &'static str = ""; }; +//! # impl GetPokemonSpecies { const ID: ShapeId = ShapeId::new("namespace#name", "namespace", "name"); }; //! // Create a `Plugin` from a HTTP `Layer` -//! let plugin = HttpLayer(layer); +//! let plugin = LayerPlugin(layer); +//! +//! scope! { +//! struct OnlyGetPokemonSpecies { +//! includes: [GetPokemonSpecies], +//! excludes: [/* The rest of the operations go here */] +//! } +//! } //! -//! // Only apply the layer to operations with name "GetPokemonSpecies" -//! let plugin = filter_by_operation_name(plugin, |name| name == GetPokemonSpecies::NAME); +//! // Only apply the layer to operations with name "GetPokemonSpecies". +//! let filtered_plugin = Scoped::new::(&plugin); +//! +//! // The same effect can be achieved at runtime. +//! let filtered_plugin = filter_by_operation(&plugin, |operation: Operation| operation == Operation::GetPokemonSpecies); //! ``` //! //! # Construct a [`Plugin`] from a closure that takes as input the operation name //! -//! ``` -//! # use aws_smithy_http_server::plugin::*; -//! // A `tower::Layer` which requires the operation name -//! struct PrintLayer { -//! name: &'static str +//! ```rust +//! # use aws_smithy_http_server::{service::*, operation::OperationShape, plugin::Plugin, shape_id::ShapeId}; +//! # pub enum Operation { CheckHealth, GetPokemonSpecies } +//! # impl Operation { fn shape_id(&self) -> ShapeId { ShapeId::new("", "", "") }} +//! # pub struct CheckHealth; +//! # pub struct GetPokemonSpecies; +//! # pub struct PokemonService; +//! # impl ServiceShape for PokemonService { +//! # const ID: ShapeId = ShapeId::new("", "", ""); +//! # const VERSION: Option<&'static str> = None; +//! # type Protocol = (); +//! # type Operations = Operation; +//! # } +//! # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +//! # impl OperationShape for GetPokemonSpecies { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +//! # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } +//! # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::GetPokemonSpecies; } +//! use aws_smithy_http_server::plugin::plugin_from_operation_fn; +//! use tower::layer::layer_fn; +//! +//! struct FooService { +//! info: String, +//! inner: S //! } //! -//! // Create a `Plugin` using `PrintLayer` -//! let plugin = plugin_from_operation_name_fn(|name| PrintLayer { name }); +//! fn map(op: Operation, inner: S) -> FooService { +//! match op { +//! Operation::CheckHealth => FooService { info: op.shape_id().name().to_string(), inner }, +//! Operation::GetPokemonSpecies => FooService { info: "bar".to_string(), inner }, +//! _ => todo!() +//! } +//! } +//! +//! // This plugin applies the `FooService` middleware around every operation. +//! let plugin = plugin_from_operation_fn(map); +//! # let _ = Plugin::::apply(&plugin, ()); +//! # let _ = Plugin::::apply(&plugin, ()); //! ``` //! //! # Combine [`Plugin`]s //! -//! ``` +//! ```no_run //! # use aws_smithy_http_server::plugin::*; -//! # let a = (); let b = (); -//! // Combine `Plugin`s `a` and `b` -//! let plugin = PluginPipeline::new() +//! # struct Foo; +//! # impl HttpMarker for Foo { } +//! # let a = Foo; let b = Foo; +//! // Combine `Plugin`s `a` and `b`. Both need to implement `HttpMarker`. +//! let plugin = HttpPlugins::new() //! .push(a) //! .push(b); //! ``` //! -//! As noted in the [`PluginPipeline`] documentation, the plugins' runtime logic is executed in registration order, +//! As noted in the [`HttpPlugins`] documentation, the plugins' runtime logic is executed in registration order, //! meaning that `a` is run _before_ `b` in the example above. //! -//! # Example Implementation +//! Similarly, you can use [`ModelPlugins`] to combine model plugins. //! -//! ```rust +//! # Example implementation of a [`Plugin`] +//! +//! The following is an example implementation of a [`Plugin`] that prints out the service's name +//! and the name of the operation that was hit every time it runs. Since it doesn't act on the HTTP +//! request nor the modeled operation input/output/errors, this plugin can be both an HTTP plugin +//! and a model plugin. In practice, however, you'd only want to register it once, as either an +//! HTTP plugin or a model plugin. +//! +//! ```no_run //! use aws_smithy_http_server::{ -//! operation::{Operation, OperationShape}, -//! plugin::{Plugin, PluginPipeline, PluginStack}, +//! operation::OperationShape, +//! service::ServiceShape, +//! plugin::{Plugin, HttpMarker, HttpPlugins, ModelMarker}, +//! shape_id::ShapeId, //! }; //! # use tower::{layer::util::Stack, Layer, Service}; //! # use std::task::{Context, Poll}; @@ -63,7 +145,8 @@ //! #[derive(Clone, Debug)] //! pub struct PrintService { //! inner: S, -//! name: &'static str, +//! service_id: ShapeId, +//! operation_id: ShapeId //! } //! //! impl Service for PrintService @@ -79,87 +162,303 @@ //! } //! //! fn call(&mut self, req: R) -> Self::Future { -//! println!("Hi {}", self.name); +//! println!("Hi {} in {}", self.operation_id.absolute(), self.service_id.absolute()); //! self.inner.call(req) //! } //! } //! -//! /// A [`Layer`] which constructs the [`PrintService`]. -//! #[derive(Debug)] -//! pub struct PrintLayer { -//! name: &'static str, -//! } -//! impl Layer for PrintLayer { -//! type Service = PrintService; -//! -//! fn layer(&self, service: S) -> Self::Service { -//! PrintService { -//! inner: service, -//! name: self.name, -//! } -//! } -//! } -//! //! /// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. //! #[derive(Debug)] //! pub struct PrintPlugin; //! -//! impl Plugin for PrintPlugin +//! impl Plugin for PrintPlugin //! where +//! Ser: ServiceShape, //! Op: OperationShape, //! { -//! type Service = S; -//! type Layer = Stack; +//! type Output = PrintService; //! -//! fn map(&self, input: Operation) -> Operation { -//! input.layer(PrintLayer { name: Op::NAME }) +//! fn apply(&self, inner: T) -> Self::Output { +//! PrintService { +//! inner, +//! service_id: Op::ID, +//! operation_id: Ser::ID, +//! } //! } //! } -//! ``` //! +//! // This plugin could be registered as an HTTP plugin and a model plugin, so we implement both +//! // marker traits. +//! +//! impl HttpMarker for PrintPlugin { } +//! impl ModelMarker for PrintPlugin { } +//! ``` mod closure; -mod either; +pub(crate) mod either; mod filter; +mod http_plugins; mod identity; mod layer; -mod pipeline; +mod model_plugins; +#[doc(hidden)] +pub mod scoped; mod stack; -use crate::operation::Operation; - -pub use closure::{plugin_from_operation_name_fn, OperationNameFn}; +pub use closure::{plugin_from_operation_fn, OperationFn}; pub use either::Either; -pub use filter::{filter_by_operation_name, FilterByOperationName}; +pub use filter::{filter_by_operation, FilterByOperation}; +pub use http_plugins::HttpPlugins; pub use identity::IdentityPlugin; -pub use layer::HttpLayer; -pub use pipeline::PluginPipeline; +pub use layer::{LayerPlugin, PluginLayer}; +pub use model_plugins::ModelPlugins; +pub use scoped::Scoped; pub use stack::PluginStack; -/// A mapping from one [`Operation`] to another. Used to modify the behavior of -/// [`Upgradable`](crate::operation::Upgradable) and therefore the resulting service builder. +/// A mapping from one [`Service`](tower::Service) to another. This should be viewed as a +/// [`Layer`](tower::Layer) parameterized by the protocol and operation. /// -/// The generics `Protocol` and `Op` allow the behavior to be parameterized. +/// The generics `Ser` and `Op` allow the behavior to be parameterized by the [Smithy service] and +/// [operation] it's applied to. /// /// See [module](crate::plugin) documentation for more information. -pub trait Plugin { +/// +/// [Smithy service]: https://smithy.io/2.0/spec/service-types.html#service +/// [operation]: https://smithy.io/2.0/spec/service-types.html#operation +pub trait Plugin { /// The type of the new [`Service`](tower::Service). - type Service; - /// The type of the new [`Layer`](tower::Layer). - type Layer; + type Output; - /// Maps an [`Operation`] to another. - fn map(&self, input: Operation) -> Operation; + /// Maps a [`Service`](tower::Service) to another. + fn apply(&self, input: T) -> Self::Output; } -impl<'a, P, Op, S, L, Pl> Plugin for &'a Pl +impl<'a, Ser, Op, T, Pl> Plugin for &'a Pl where - Pl: Plugin, + Pl: Plugin, { - type Service = Pl::Service; - type Layer = Pl::Layer; + type Output = Pl::Output; - fn map(&self, input: Operation) -> Operation { - >::map(*self, input) + fn apply(&self, inner: T) -> Self::Output { + >::apply(self, inner) } } + +/// A HTTP plugin is a plugin that acts on the HTTP request before it is deserialized, and acts on +/// the HTTP response after it is serialized. +/// +/// This trait is a _marker_ trait to indicate that a plugin can be registered as an HTTP plugin. +/// +/// Compare with [`ModelMarker`] in the [module](crate::plugin) documentation, which contains an +/// example implementation too. +pub trait HttpMarker {} +impl<'a, Pl> HttpMarker for &'a Pl where Pl: HttpMarker {} + +/// A model plugin is a plugin that acts on the modeled operation input after it is deserialized, +/// and acts on the modeled operation output or the modeled operation error before it is +/// serialized. +/// +/// This trait is a _marker_ trait to indicate that a plugin can be registered as a model plugin. +/// +/// Compare with [`HttpMarker`] in the [module](crate::plugin) documentation. +/// +/// # Example implementation of a model plugin +/// +/// Model plugins are most useful when you really need to rely on the actual shape of your +/// modeled operation input, operation output, and/or operation errors. For this reason, most +/// model plugins' implementation are _operation-specific_: somewhere in the type signature +/// of their definition, they'll rely on a operation shape's types. It is therefore important +/// that you scope application of model plugins to the operations they are meant to work on, via +/// [`Scoped`](crate::plugin::Scoped) or [`filter_by_operation`](crate::plugin::filter_by_operation). +/// +/// Below is an example implementation of a model plugin that can only be applied to the +/// `CheckHealth` operation: note how in the `Service` trait implementation, we require access to +/// the operation's input, where we log the `health_info` field. +/// +/// ```no_run +/// use std::marker::PhantomData; +/// +/// use aws_smithy_http_server::{operation::OperationShape, plugin::{ModelMarker, Plugin}}; +/// use tower::Service; +/// # pub struct SimpleService; +/// # pub struct CheckHealth; +/// # pub struct CheckHealthInput { +/// # health_info: (), +/// # } +/// # pub struct CheckHealthOutput; +/// # impl aws_smithy_http_server::operation::OperationShape for CheckHealth { +/// # const ID: aws_smithy_http_server::shape_id::ShapeId = aws_smithy_http_server::shape_id::ShapeId::new( +/// # "com.amazonaws.simple#CheckHealth", +/// # "com.amazonaws.simple", +/// # "CheckHealth", +/// # ); +/// # type Input = CheckHealthInput; +/// # type Output = CheckHealthOutput; +/// # type Error = std::convert::Infallible; +/// # } +/// +/// /// A model plugin that can only be applied to the `CheckHealth` operation. +/// pub struct CheckHealthPlugin { +/// pub _exts: PhantomData, +/// } +/// +/// impl CheckHealthPlugin { +/// pub fn new() -> Self { +/// Self { _exts: PhantomData } +/// } +/// } +/// +/// impl Plugin for CheckHealthPlugin { +/// type Output = CheckHealthService; +/// +/// fn apply(&self, input: T) -> Self::Output { +/// CheckHealthService { +/// inner: input, +/// _exts: PhantomData, +/// } +/// } +/// } +/// +/// impl ModelMarker for CheckHealthPlugin { } +/// +/// #[derive(Clone)] +/// pub struct CheckHealthService { +/// inner: S, +/// _exts: PhantomData, +/// } +/// +/// impl Service<(::Input, Exts)> for CheckHealthService +/// where +/// S: Service<(::Input, Exts)>, +/// { +/// type Response = S::Response; +/// type Error = S::Error; +/// type Future = S::Future; +/// +/// fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { +/// self.inner.poll_ready(cx) +/// } +/// +/// fn call(&mut self, req: (::Input, Exts)) -> Self::Future { +/// let (input, _exts) = &req; +/// +/// // We have access to `CheckHealth`'s modeled operation input! +/// dbg!(&input.health_info); +/// +/// self.inner.call(req) +/// } +/// } +/// +/// // In `main.rs` or wherever we register plugins, we have to make sure we only apply this plugin +/// // to the the only operation it can be applied to, the `CheckHealth` operation. If we apply the +/// // plugin to other operations, we will get a compilation error. +/// +/// use aws_smithy_http_server::plugin::Scoped; +/// use aws_smithy_http_server::scope; +/// +/// pub fn main() { +/// scope! { +/// struct OnlyCheckHealth { +/// includes: [CheckHealth], +/// excludes: [/* The rest of the operations go here */] +/// } +/// } +/// +/// let model_plugin = CheckHealthPlugin::new(); +/// # _foo(&model_plugin); +/// +/// // Scope the plugin to the `CheckHealth` operation. +/// let scoped_plugin = Scoped::new::(model_plugin); +/// # fn _foo(model_plugin: &CheckHealthPlugin<()>) {} +/// } +/// ``` +/// +/// If you are a service owner and don't care about giving a name to the model plugin, you can +/// simplify this down to: +/// +/// ```no_run +/// use std::marker::PhantomData; +/// +/// use aws_smithy_http_server::operation::OperationShape; +/// use tower::Service; +/// # pub struct SimpleService; +/// # pub struct CheckHealth; +/// # pub struct CheckHealthInput { +/// # health_info: (), +/// # } +/// # pub struct CheckHealthOutput; +/// # impl aws_smithy_http_server::operation::OperationShape for CheckHealth { +/// # const ID: aws_smithy_http_server::shape_id::ShapeId = aws_smithy_http_server::shape_id::ShapeId::new( +/// # "com.amazonaws.simple#CheckHealth", +/// # "com.amazonaws.simple", +/// # "CheckHealth", +/// # ); +/// # type Input = CheckHealthInput; +/// # type Output = CheckHealthOutput; +/// # type Error = std::convert::Infallible; +/// # } +/// +/// #[derive(Clone)] +/// pub struct CheckHealthService { +/// inner: S, +/// _exts: PhantomData, +/// } +/// +/// impl Service<(::Input, Exts)> for CheckHealthService +/// where +/// S: Service<(::Input, Exts)>, +/// { +/// type Response = S::Response; +/// type Error = S::Error; +/// type Future = S::Future; +/// +/// fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { +/// self.inner.poll_ready(cx) +/// } +/// +/// fn call(&mut self, req: (::Input, Exts)) -> Self::Future { +/// let (input, _exts) = &req; +/// +/// // We have access to `CheckHealth`'s modeled operation input! +/// dbg!(&input.health_info); +/// +/// self.inner.call(req) +/// } +/// } +/// +/// // In `main.rs`: +/// +/// use aws_smithy_http_server::plugin::LayerPlugin; +/// use aws_smithy_http_server::plugin::Scoped; +/// use aws_smithy_http_server::scope; +/// +/// fn new_check_health_service(inner: S) -> CheckHealthService { +/// CheckHealthService { +/// inner, +/// _exts: PhantomData, +/// } +/// } +/// +/// pub fn main() { +/// scope! { +/// struct OnlyCheckHealth { +/// includes: [CheckHealth], +/// excludes: [/* The rest of the operations go here */] +/// } +/// } +/// +/// # fn new_check_health_service(inner: ()) -> CheckHealthService<(), ()> { +/// # CheckHealthService { +/// # inner, +/// # _exts: PhantomData, +/// # } +/// # } +/// let layer = tower::layer::layer_fn(new_check_health_service); +/// let model_plugin = LayerPlugin(layer); +/// +/// // Scope the plugin to the `CheckHealth` operation. +/// let scoped_plugin = Scoped::new::(model_plugin); +/// } +/// ``` +pub trait ModelMarker {} +impl<'a, Pl> ModelMarker for &'a Pl where Pl: ModelMarker {} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/model_plugins.rs b/rust-runtime/aws-smithy-http-server/src/plugin/model_plugins.rs new file mode 100644 index 0000000000..05dc8568d7 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/plugin/model_plugins.rs @@ -0,0 +1,94 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// If you make any updates to this file (including Rust docs), make sure you make them to +// `http_plugins.rs` too! + +use crate::plugin::{IdentityPlugin, Plugin, PluginStack}; + +use super::{LayerPlugin, ModelMarker}; + +/// A wrapper struct for composing model plugins. +/// It operates identically to [`HttpPlugins`](crate::plugin::HttpPlugins); see its documentation. +#[derive(Debug)] +pub struct ModelPlugins

(pub(crate) P); + +impl Default for ModelPlugins { + fn default() -> Self { + Self(IdentityPlugin) + } +} + +impl ModelPlugins { + /// Create an empty [`ModelPlugins`]. + /// + /// You can use [`ModelPlugins::push`] to add plugins to it. + pub fn new() -> Self { + Self::default() + } +} + +impl

ModelPlugins

{ + /// Apply a new model plugin after the ones that have already been registered. + /// + /// ```rust + /// use aws_smithy_http_server::plugin::ModelPlugins; + /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; + /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; + /// + /// let model_plugins = ModelPlugins::new().push(LoggingPlugin).push(MetricsPlugin); + /// ``` + /// + /// The plugins' runtime logic is executed in registration order. + /// In our example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. + /// + /// ## Implementation notes + /// + /// Plugins are applied to the underlying [`Service`](tower::Service) in opposite order compared + /// to their registration order. + /// + /// As an example: + /// + /// ```rust,compile_fail + /// #[derive(Debug)] + /// pub struct PrintPlugin; + /// + /// impl Plugin for PrintPlugin + /// // [...] + /// { + /// // [...] + /// fn apply(&self, inner: T) -> Self::Service { + /// PrintService { + /// inner, + /// service_id: Ser::ID, + /// operation_id: Op::ID + /// } + /// } + /// } + /// ``` + // We eagerly require `NewPlugin: ModelMarker`, despite not really needing it, because compiler + // errors get _substantially_ better if the user makes a mistake. + pub fn push(self, new_plugin: NewPlugin) -> ModelPlugins> { + ModelPlugins(PluginStack::new(new_plugin, self.0)) + } + + /// Applies a single [`tower::Layer`] to all operations _before_ they are deserialized. + pub fn layer(self, layer: L) -> ModelPlugins, P>> { + ModelPlugins(PluginStack::new(LayerPlugin(layer), self.0)) + } +} + +impl Plugin for ModelPlugins +where + InnerPlugin: Plugin, +{ + type Output = InnerPlugin::Output; + + fn apply(&self, input: T) -> Self::Output { + self.0.apply(input) + } +} + +impl ModelMarker for ModelPlugins where InnerPlugin: ModelMarker {} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs deleted file mode 100644 index 7b390fc903..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::operation::Operation; -use crate::plugin::{IdentityPlugin, Plugin, PluginStack}; - -/// A wrapper struct for composing [`Plugin`]s. -/// It is used as input for the `builder_with_plugins` method on the generate service struct -/// (e.g. `PokemonService::builder_with_plugins`). -/// -/// ## Applying plugins in a sequence -/// -/// You can use the [`push`](PluginPipeline::push) method to apply a new plugin after the ones that -/// have already been registered. -/// -/// ```rust -/// use aws_smithy_http_server::plugin::PluginPipeline; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; -/// -/// let pipeline = PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin); -/// ``` -/// -/// The plugins' runtime logic is executed in registration order. -/// In our example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. -/// -/// ## Wrapping the current plugin pipeline -/// -/// From time to time, you might have a need to transform the entire pipeline that has been built -/// so far - e.g. you only want to apply those plugins for a specific operation. -/// -/// `PluginPipeline` is itself a [`Plugin`]: you can apply any transformation that expects a -/// [`Plugin`] to an entire pipeline. In this case, we want to use -/// [`filter_by_operation_name`](crate::plugin::filter_by_operation_name) to limit the scope of -/// the logging and metrics plugins to the `CheckHealth` operation: -/// -/// ```rust -/// use aws_smithy_http_server::plugin::{filter_by_operation_name, PluginPipeline}; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; -/// # struct CheckHealth; -/// # impl CheckHealth { const NAME: &'static str = "MyName"; } -/// -/// // The logging and metrics plugins will only be applied to the `CheckHealth` operation. -/// let operation_specific_pipeline = filter_by_operation_name( -/// PluginPipeline::new() -/// .push(LoggingPlugin) -/// .push(MetricsPlugin), -/// |name| name == CheckHealth::NAME -/// ); -/// let pipeline = PluginPipeline::new() -/// .push(operation_specific_pipeline) -/// // The auth plugin will be applied to all operations -/// .push(AuthPlugin); -/// ``` -/// -/// ## Concatenating two plugin pipelines -/// -/// `PluginPipeline` is a good way to bundle together multiple plugins, ensuring they are all -/// registered in the correct order. -/// -/// Since `PluginPipeline` is itself a [`Plugin`], you can use the [`push`](PluginPipeline::push) to -/// append, at once, all the plugins in another pipeline to the current pipeline: -/// -/// ```rust -/// use aws_smithy_http_server::plugin::{IdentityPlugin, PluginPipeline, PluginStack}; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; -/// -/// pub fn get_bundled_pipeline() -> PluginPipeline>> { -/// PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin) -/// } -/// -/// let pipeline = PluginPipeline::new() -/// .push(AuthPlugin) -/// .push(get_bundled_pipeline()); -/// ``` -/// -/// ## Providing custom methods on `PluginPipeline` -/// -/// You use an **extension trait** to add custom methods on `PluginPipeline`. -/// -/// This is a simple example using `AuthPlugin`: -/// -/// ```rust -/// use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; -/// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; -/// -/// pub trait AuthPluginExt { -/// fn with_auth(self) -> PluginPipeline>; -/// } -/// -/// impl AuthPluginExt for PluginPipeline { -/// fn with_auth(self) -> PluginPipeline> { -/// self.push(AuthPlugin) -/// } -/// } -/// -/// let pipeline = PluginPipeline::new() -/// .push(LoggingPlugin) -/// // Our custom method! -/// .with_auth(); -/// ``` -pub struct PluginPipeline

(P); - -impl Default for PluginPipeline { - fn default() -> Self { - Self(IdentityPlugin) - } -} - -impl PluginPipeline { - /// Create an empty [`PluginPipeline`]. - /// - /// You can use [`PluginPipeline::push`] to add plugins to it. - pub fn new() -> Self { - Self::default() - } -} - -impl

PluginPipeline

{ - /// Apply a new plugin after the ones that have already been registered. - /// - /// ```rust - /// use aws_smithy_http_server::plugin::PluginPipeline; - /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; - /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; - /// - /// let pipeline = PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin); - /// ``` - /// - /// The plugins' runtime logic is executed in registration order. - /// In our example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. - /// - /// ## Implementation notes - /// - /// Plugins are applied to the underlying [`Operation`] in opposite order compared - /// to their registration order. - /// But most [`Plugin::map`] implementations desugar to appending a layer to [`Operation`], - /// usually via [`Operation::layer`]. - /// As an example: - /// - /// ```rust,compile_fail - /// #[derive(Debug)] - /// pub struct PrintPlugin; - /// - /// impl Plugin for PrintPlugin - /// // [...] - /// { - /// // [...] - /// fn map(&self, input: Operation) -> Operation { - /// input.layer(PrintLayer { name: Op::NAME }) - /// } - /// } - /// ``` - /// - /// The layer that is registered **last** via [`Operation::layer`] is the one that gets executed - /// **first** at runtime when a new request comes in, since it _wraps_ the underlying service. - /// - /// This is why plugins in [`PluginPipeline`] are applied in opposite order compared to their - /// registration order: this ensures that, _at runtime_, their logic is executed - /// in registration order. - pub fn push(self, new_plugin: NewPlugin) -> PluginPipeline> { - PluginPipeline(PluginStack::new(new_plugin, self.0)) - } -} - -impl Plugin for PluginPipeline -where - InnerPlugin: Plugin, -{ - type Service = InnerPlugin::Service; - type Layer = InnerPlugin::Layer; - - fn map(&self, input: Operation) -> Operation { - self.0.map(input) - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs new file mode 100644 index 0000000000..d4b7e82e51 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs @@ -0,0 +1,192 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::marker::PhantomData; + +use super::{HttpMarker, ModelMarker, Plugin}; + +/// Marker struct for `true`. +/// +/// Implements [`ConditionalApply`] which applies the [`Plugin`]. +pub struct True; + +/// Marker struct for `false`. +/// +/// Implements [`ConditionalApply`] which does nothing. +pub struct False; + +/// Conditionally applies a [`Plugin`] `Pl` to some service `S`. +/// +/// See [`True`] and [`False`]. +pub trait ConditionalApply { + type Service; + + fn apply(plugin: &Pl, svc: T) -> Self::Service; +} + +impl ConditionalApply for True +where + Pl: Plugin, +{ + type Service = Pl::Output; + + fn apply(plugin: &Pl, input: T) -> Self::Service { + plugin.apply(input) + } +} + +impl ConditionalApply for False { + type Service = T; + + fn apply(_plugin: &Pl, input: T) -> Self::Service { + input + } +} + +/// A [`Plugin`] which scopes the application of an inner [`Plugin`]. +/// +/// In cases where operation selection must be performed at runtime [`filter_by_operation`](crate::plugin::filter_by_operation) +/// can be used. +/// +/// Operations within the scope will have the inner [`Plugin`] applied. +/// +/// # Example +/// +/// ```rust +/// # use aws_smithy_http_server::{scope, plugin::Scoped}; +/// # struct OperationA; struct OperationB; struct OperationC; +/// # let plugin = (); +/// +/// // Define a scope over a service with 3 operations +/// scope! { +/// struct OnlyAB { +/// includes: [OperationA, OperationB], +/// excludes: [OperationC] +/// } +/// } +/// +/// // Create a scoped plugin +/// let scoped_plugin = Scoped::new::(plugin); +/// ``` +pub struct Scoped { + scope: PhantomData, + plugin: Pl, +} + +impl Scoped<(), Pl> { + /// Creates a new [`Scoped`] from a `Scope` and [`Plugin`]. + pub fn new(plugin: Pl) -> Scoped { + Scoped { + scope: PhantomData, + plugin, + } + } +} + +/// A trait marking which operations are in scope via the associated type [`Membership::Contains`]. +pub trait Membership { + type Contains; +} + +impl Plugin for Scoped +where + Scope: Membership, + Scope::Contains: ConditionalApply, +{ + type Output = >::Service; + + fn apply(&self, input: T) -> Self::Output { + >::apply(&self.plugin, input) + } +} + +impl HttpMarker for Scoped where Pl: HttpMarker {} +impl ModelMarker for Scoped where Pl: ModelMarker {} + +/// A macro to help with scoping [plugins](crate::plugin) to a subset of all operations. +/// +/// The scope must partition _all_ operations, that is, each and every operation must be included or excluded, but not +/// both. +/// +/// The generated server SDK exports a similar `scope` macro which is aware of a service's operations and can complete +/// underspecified scopes automatically. +/// +/// # Example +/// +/// For a service with three operations: `OperationA`, `OperationB`, `OperationC`. +/// +/// ```rust +/// # use aws_smithy_http_server::scope; +/// # struct OperationA; struct OperationB; struct OperationC; +/// scope! { +/// struct OnlyAB { +/// includes: [OperationA, OperationB], +/// excludes: [OperationC] +/// } +/// } +/// ``` +#[macro_export] +macro_rules! scope { + ( + $(#[$attrs:meta])* + $vis:vis struct $name:ident { + includes: [$($include:ty),*], + excludes: [$($exclude:ty),*] + } + ) => { + $(#[$attrs])* + $vis struct $name; + + $( + impl $crate::plugin::scoped::Membership<$include> for $name { + type Contains = $crate::plugin::scoped::True; + } + )* + $( + impl $crate::plugin::scoped::Membership<$exclude> for $name { + type Contains = $crate::plugin::scoped::False; + } + )* + }; +} + +#[cfg(test)] +mod tests { + use crate::plugin::Plugin; + + use super::Scoped; + + struct OperationA; + struct OperationB; + + scope! { + /// Includes A, not B. + pub struct AuthScope { + includes: [OperationA], + excludes: [OperationB] + } + } + + struct MockPlugin; + + impl Plugin for MockPlugin { + type Output = String; + + fn apply(&self, svc: u32) -> Self::Output { + svc.to_string() + } + } + + #[test] + fn scope() { + let plugin = MockPlugin; + let scoped_plugin = Scoped::new::(plugin); + + let out: String = Plugin::<(), OperationA, _>::apply(&scoped_plugin, 3_u32); + assert_eq!(out, "3".to_string()); + let out: u32 = Plugin::<(), OperationB, _>::apply(&scoped_plugin, 3_u32); + assert_eq!(out, 3); + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs b/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs index a68e3086d3..6c96ebaca0 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs @@ -3,15 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::operation::Operation; - -use super::Plugin; +use super::{HttpMarker, ModelMarker, Plugin}; /// A wrapper struct which composes an `Inner` and an `Outer` [`Plugin`]. /// /// The `Inner::map` is run _then_ the `Outer::map`. /// -/// Note that the primary tool for composing plugins is [`PluginPipeline`](crate::plugin::PluginPipeline). +/// Note that the primary tool for composing HTTP plugins is +/// [`HttpPlugins`](crate::plugin::HttpPlugins), and the primary tool for composing HTTP plugins is +/// [`ModelPlugins`](crate::plugin::ModelPlugins); if you are an application writer, you should +/// prefer composing plugins using these. pub struct PluginStack { inner: Inner, outer: Outer, @@ -24,16 +25,29 @@ impl PluginStack { } } -impl Plugin for PluginStack +impl Plugin for PluginStack where - Inner: Plugin, - Outer: Plugin, + Inner: Plugin, + Outer: Plugin, { - type Service = Outer::Service; - type Layer = Outer::Layer; + type Output = Outer::Output; - fn map(&self, input: Operation) -> Operation { - let inner = self.inner.map(input); - self.outer.map(inner) + fn apply(&self, input: T) -> Self::Output { + let svc = self.inner.apply(input); + self.outer.apply(svc) } } + +impl HttpMarker for PluginStack +where + Inner: HttpMarker, + Outer: HttpMarker, +{ +} + +impl ModelMarker for PluginStack +where + Inner: ModelMarker, + Outer: ModelMarker, +{ +} diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs deleted file mode 100644 index c47057f661..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use strum_macros::Display; - -use crate::rejection::MissingContentTypeReason; - -#[derive(Debug, Display)] -pub enum ResponseRejection { - InvalidHttpStatusCode, - Serialization(crate::Error), - Http(crate::Error), -} - -impl std::error::Error for ResponseRejection {} - -convert_to_response_rejection!(aws_smithy_http::operation::error::SerializationError, Serialization); -convert_to_response_rejection!(http::Error, Http); - -#[derive(Debug, Display)] -pub enum RequestRejection { - HttpBody(crate::Error), - MissingContentType(MissingContentTypeReason), - JsonDeserialize(crate::Error), - ConstraintViolation(String), -} - -impl std::error::Error for RequestRejection {} - -impl From for RequestRejection { - fn from(_err: std::convert::Infallible) -> Self { - match _err {} - } -} - -impl From for RequestRejection { - fn from(e: MissingContentTypeReason) -> Self { - Self::MissingContentType(e) - } -} - -convert_to_request_rejection!(aws_smithy_json::deserialize::error::DeserializeError, JsonDeserialize); - -convert_to_request_rejection!(hyper::Error, HttpBody); - -convert_to_request_rejection!(Box, HttpBody); diff --git a/rust-runtime/aws-smithy-http-server/src/proto/mod.rs b/rust-runtime/aws-smithy-http-server/src/proto/mod.rs deleted file mode 100644 index 26fb17d893..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/proto/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -pub mod aws_json; -pub mod aws_json_10; -pub mod aws_json_11; -pub mod rest; -pub mod rest_json_1; -pub mod rest_xml; - -#[cfg(test)] -pub mod test_helpers { - use http::{HeaderMap, Method, Request}; - - /// Helper function to build a `Request`. Used in other test modules. - pub fn req(method: &Method, uri: &str, headers: Option) -> Request<()> { - let mut r = Request::builder().method(method).uri(uri).body(()).unwrap(); - if let Some(headers) = headers { - *r.headers_mut() = headers - } - r - } - - // Returns a `Response`'s body as a `String`, without consuming the response. - pub async fn get_body_as_string(body: B) -> String - where - B: http_body::Body + std::marker::Unpin, - B::Error: std::fmt::Debug, - { - let body_bytes = hyper::body::to_bytes(body).await.unwrap(); - String::from(std::str::from_utf8(&body_bytes).unwrap()) - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs deleted file mode 100644 index 2e3366f0b6..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! This module hosts _exactly_ the same as [`crate::proto::rest_json_1::rejection`], expect that -//! [`crate::proto::rest_json_1::rejection::RequestRejection::JsonDeserialize`] is swapped for -//! [`RequestRejection::XmlDeserialize`]. - -use strum_macros::Display; - -#[derive(Debug, Display)] -pub enum ResponseRejection { - InvalidHttpStatusCode, - Build(crate::Error), - Serialization(crate::Error), - Http(crate::Error), -} - -impl std::error::Error for ResponseRejection {} - -convert_to_response_rejection!(aws_smithy_http::operation::error::BuildError, Build); -convert_to_response_rejection!(aws_smithy_http::operation::error::SerializationError, Serialization); -convert_to_response_rejection!(http::Error, Http); - -#[derive(Debug, Display)] -pub enum RequestRejection { - HttpBody(crate::Error), - - MissingContentType(MissingContentTypeReason), - - /// Used when failing to deserialize the HTTP body's bytes into a XML conforming to the modeled - /// input it should represent. - XmlDeserialize(crate::Error), - - HeaderParse(crate::Error), - - UriPatternGreedyLabelPostfixNotFound, - UriPatternMismatch(crate::Error), - - InvalidUtf8(crate::Error), - - DateTimeParse(crate::Error), - - PrimitiveParse(crate::Error), - - ConstraintViolation(String), -} - -#[derive(Debug, Display)] -pub enum MissingContentTypeReason { - HeadersTakenByAnotherExtractor, - NoContentTypeHeader, - ToStrError(http::header::ToStrError), - MimeParseError(mime::FromStrError), - UnexpectedMimeType { - expected_mime: Option, - found_mime: Option, - }, -} - -impl std::error::Error for RequestRejection {} - -impl From for RequestRejection { - fn from(_err: std::convert::Infallible) -> Self { - match _err {} - } -} - -impl From for RequestRejection { - fn from(e: MissingContentTypeReason) -> Self { - Self::MissingContentType(e) - } -} - -convert_to_request_rejection!(aws_smithy_xml::decode::XmlDecodeError, XmlDeserialize); -convert_to_request_rejection!(aws_smithy_http::header::ParseError, HeaderParse); -convert_to_request_rejection!(aws_smithy_types::date_time::DateTimeParseError, DateTimeParse); -convert_to_request_rejection!(aws_smithy_types::primitive::PrimitiveParseError, PrimitiveParse); -convert_to_request_rejection!(serde_urlencoded::de::Error, InvalidUtf8); - -impl From>> for RequestRejection { - fn from(err: nom::Err>) -> Self { - Self::UriPatternMismatch(crate::Error::new(err.to_owned())) - } -} - -convert_to_request_rejection!(std::str::Utf8Error, InvalidUtf8); - -convert_to_request_rejection!(hyper::Error, HttpBody); - -convert_to_request_rejection!(Box, HttpBody); diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/rejection.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/rejection.rs new file mode 100644 index 0000000000..491e865dd6 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/rejection.rs @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::rejection::MissingContentTypeReason; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ResponseRejection { + #[error("error serializing JSON-encoded body: {0}")] + Serialization(#[from] aws_smithy_http::operation::error::SerializationError), + #[error("error building HTTP response: {0}")] + HttpBuild(#[from] http::Error), +} + +#[derive(Debug, Error)] +pub enum RequestRejection { + #[error("error converting non-streaming body to bytes: {0}")] + BufferHttpBodyBytes(crate::Error), + #[error("request contains invalid value for `Accept` header")] + NotAcceptable, + #[error("expected `Content-Type` header not found: {0}")] + MissingContentType(#[from] MissingContentTypeReason), + #[error("error deserializing request HTTP body as JSON: {0}")] + JsonDeserialize(#[from] aws_smithy_json::deserialize::error::DeserializeError), + #[error("request does not adhere to modeled constraints: {0}")] + ConstraintViolation(String), +} + +impl From for RequestRejection { + fn from(_err: std::convert::Infallible) -> Self { + match _err {} + } +} + +convert_to_request_rejection!(hyper::Error, BufferHttpBodyBytes); +convert_to_request_rejection!(Box, BufferHttpBodyBytes); diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs similarity index 98% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs index 4474dfb600..2cd48f8e61 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs @@ -121,7 +121,7 @@ impl FromIterator<(String, S)> for AwsJsonRouter { #[cfg(test)] mod tests { use super::*; - use crate::{proto::test_helpers::req, routing::Router}; + use crate::{protocol::test_helpers::req, routing::Router}; use http::{HeaderMap, HeaderValue, Method}; use pretty_assertions::assert_eq; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs similarity index 96% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json/runtime_error.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs index 2e92befa81..8970d16f30 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::proto::aws_json_11::AwsJson1_1; +use crate::protocol::aws_json_11::AwsJson1_1; use crate::response::IntoResponse; use crate::runtime_error::{InternalFailureException, INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE}; -use crate::{extension::RuntimeErrorExtension, proto::aws_json_10::AwsJson1_0}; +use crate::{extension::RuntimeErrorExtension, protocol::aws_json_10::AwsJson1_0}; use http::StatusCode; use super::rejection::{RequestRejection, ResponseRejection}; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs similarity index 92% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs index 50e73e2198..f3093685e2 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs @@ -5,13 +5,12 @@ use crate::body::{empty, BoxBody}; use crate::extension::RuntimeErrorExtension; -use crate::proto::aws_json::router::Error; use crate::response::IntoResponse; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::AwsJson1_0; -pub use crate::proto::aws_json::router::*; +pub use crate::protocol::aws_json::router::*; impl IntoResponse for Error { fn into_response(self) -> http::Response { diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs similarity index 92% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs index c7d1176e98..898d8c29af 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs @@ -5,13 +5,12 @@ use crate::body::{empty, BoxBody}; use crate::extension::RuntimeErrorExtension; -use crate::proto::aws_json::router::Error; use crate::response::IntoResponse; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::AwsJson1_1; -pub use crate::proto::aws_json::router::*; +pub use crate::protocol::aws_json::router::*; impl IntoResponse for Error { fn into_response(self) -> http::Response { diff --git a/rust-runtime/aws-smithy-http-server/src/protocols.rs b/rust-runtime/aws-smithy-http-server/src/protocol/mod.rs similarity index 80% rename from rust-runtime/aws-smithy-http-server/src/protocols.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/mod.rs index a2428a768b..004d32a38b 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocols.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/mod.rs @@ -3,10 +3,40 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Protocol helpers. +pub mod aws_json; +pub mod aws_json_10; +pub mod aws_json_11; +pub mod rest; +pub mod rest_json_1; +pub mod rest_xml; + use crate::rejection::MissingContentTypeReason; use http::HeaderMap; +#[cfg(test)] +pub mod test_helpers { + use http::{HeaderMap, Method, Request}; + + /// Helper function to build a `Request`. Used in other test modules. + pub fn req(method: &Method, uri: &str, headers: Option) -> Request<()> { + let mut r = Request::builder().method(method).uri(uri).body(()).unwrap(); + if let Some(headers) = headers { + *r.headers_mut() = headers + } + r + } + + // Returns a `Response`'s body as a `String`, without consuming the response. + pub async fn get_body_as_string(body: B) -> String + where + B: http_body::Body + std::marker::Unpin, + B::Error: std::fmt::Debug, + { + let body_bytes = hyper::body::to_bytes(body).await.unwrap(); + String::from(std::str::from_utf8(&body_bytes).unwrap()) + } +} + /// When there are no modeled inputs, /// a request body is empty and the content-type request header must not be set #[allow(clippy::result_large_err)] @@ -34,7 +64,7 @@ fn parse_content_type(headers: &HeaderMap) -> Result, @@ -66,15 +96,10 @@ pub fn content_type_header_classifier( Ok(()) } -#[allow(deprecated)] -pub fn accept_header_classifier(headers: &HeaderMap, content_type: &'static str) -> bool { +pub fn accept_header_classifier(headers: &HeaderMap, content_type: &mime::Mime) -> bool { if !headers.contains_key(http::header::ACCEPT) { return true; } - // Must be of the form: type/subtype - let content_type = content_type - .parse::() - .expect("BUG: MIME parsing failed, content_type is not valid"); headers .get_all(http::header::ACCEPT) .into_iter() @@ -105,7 +130,6 @@ pub fn accept_header_classifier(headers: &HeaderMap, content_type: &'static str) }) } -#[allow(deprecated)] #[cfg(test)] mod tests { use super::*; @@ -197,41 +221,62 @@ mod tests { #[test] fn valid_accept_header_classifier_multiple_values() { let valid_request = req_accept("text/strings, application/json, invalid"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } #[test] fn invalid_accept_header_classifier() { let invalid_request = req_accept("text/invalid, invalid, invalid/invalid"); - assert!(!accept_header_classifier(&invalid_request, "application/json")); + assert!(!accept_header_classifier( + &invalid_request, + &"application/json".parse().unwrap() + )); } #[test] fn valid_accept_header_classifier_star() { let valid_request = req_accept("application/*"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } #[test] fn valid_accept_header_classifier_star_star() { let valid_request = req_accept("*/*"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } #[test] fn valid_empty_accept_header_classifier() { - assert!(accept_header_classifier(&HeaderMap::new(), "application/json")); + assert!(accept_header_classifier( + &HeaderMap::new(), + &"application/json".parse().unwrap() + )); } #[test] fn valid_accept_header_classifier_with_params() { let valid_request = req_accept("application/json; q=30, */*"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } #[test] fn valid_accept_header_classifier() { let valid_request = req_accept("application/json"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } } diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/rest/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs similarity index 99% rename from rust-runtime/aws-smithy-http-server/src/proto/rest/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs index 1d55f676d6..fbbb98ee81 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs @@ -112,7 +112,7 @@ impl FromIterator<(RequestSpec, S)> for RestRouter { #[cfg(test)] mod tests { use super::*; - use crate::{proto::test_helpers::req, routing::request_spec::*}; + use crate::{protocol::test_helpers::req, routing::request_spec::*}; use http::Method; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/rejection.rs similarity index 69% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/rejection.rs index 848a8b768a..ea0c1d6aed 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/rejection.rs @@ -28,7 +28,7 @@ //! into responses. They serve as a mechanism to keep track of all the possible errors that can //! occur when processing a request or a response, in far more detail than what AWS protocols need //! to. This is why they are so granular: other (possibly protocol-specific) error types (like -//! [`crate::proto::rest_json_1::runtime_error::RuntimeError`]) can "group" them when exposing +//! [`crate::protocol::rest_json_1::runtime_error::RuntimeError`]) can "group" them when exposing //! errors to clients while the framework does not need to sacrifice fidelity in private error //! handling routines, and future-proofing itself at the same time (for example, we might want to //! record metrics about rejection types). @@ -36,39 +36,47 @@ //! Rejection types implement [`std::error::Error`], and some take in type-erased boxed errors //! (`crate::Error`) to represent their underlying causes, so they can be composed with other types //! that take in (possibly type-erased) [`std::error::Error`]s, like -//! [`crate::proto::rest_json_1::runtime_error::RuntimeError`], thus allowing us to represent the +//! [`crate::protocol::rest_json_1::runtime_error::RuntimeError`], thus allowing us to represent the //! full error chain. //! -//! This module hosts rejection types _specific_ to the [`crate::proto::rest_json_1`] protocol, but +//! This module hosts rejection types _specific_ to the [`crate::protocol::rest_json_1`] protocol, but //! the paragraphs above apply to _all_ protocol-specific rejection types. //! //! Similarly, rejection type variants are exhaustively documented solely in this module if they have //! direct counterparts in other protocols. This is to avoid documentation getting out of date. //! -//! Consult `crate::proto::$protocolName::rejection` for rejection types for other protocols. - -use strum_macros::Display; +//! Consult `crate::protocol::$protocolName::rejection` for rejection types for other protocols. use crate::rejection::MissingContentTypeReason; +use std::num::TryFromIntError; +use thiserror::Error; /// Errors that can occur when serializing the operation output provided by the service implementer /// into an HTTP response. -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum ResponseRejection { /// Used when the service implementer provides an integer outside the 100-999 range for a /// member targeted by `httpResponseCode`. /// See . - InvalidHttpStatusCode, + #[error("invalid bound HTTP status code; status codes must be inside the 100-999 range: {0}")] + InvalidHttpStatusCode(TryFromIntError), - /// Used when an invalid HTTP header value (a value that cannot be parsed as an - /// `[http::header::HeaderValue]`) is provided for a shape member bound to an HTTP header with + /// Used when an invalid HTTP header name (a value that cannot be parsed as an + /// [`http::header::HeaderName`]) or HTTP header value (a value that cannot be parsed as an + /// [`http::header::HeaderValue`]) is provided for a shape member bound to an HTTP header with /// `httpHeader` or `httpPrefixHeaders`. /// Used when failing to serialize an `httpPayload`-bound struct into an HTTP response body. - Build(crate::Error), - - /// Used when failing to serialize a struct into a `String` for the HTTP response body (for - /// example, converting a struct into a JSON-encoded `String`). - Serialization(crate::Error), + #[error("error building HTTP response: {0}")] + Build(#[from] aws_smithy_http::operation::error::BuildError), + + /// Used when failing to serialize a struct into a `String` for the JSON-encoded HTTP response + /// body. + /// Fun fact: as of writing, this can only happen when date formatting + /// (`aws_smithy_types::date_time::DateTime:fmt`) fails, which can only happen if the + /// supplied timestamp is outside of the valid range when formatting using RFC-3339, i.e. a + /// date outside the `0001-01-01T00:00:00.000Z`-`9999-12-31T23:59:59.999Z` range is supplied. + #[error("error serializing JSON-encoded body: {0}")] + Serialization(#[from] aws_smithy_http::operation::error::SerializationError), /// Used when consuming an [`http::response::Builder`] into the constructed [`http::Response`] /// when calling [`http::response::Builder::body`]. @@ -76,15 +84,10 @@ pub enum ResponseRejection { /// `[http::header::HeaderValue]`) is used for the protocol-specific response `Content-Type` /// header, or for additional protocol-specific headers (like `X-Amzn-Errortype` to signal /// errors in RestJson1). - Http(crate::Error), + #[error("error building HTTP response: {0}")] + HttpBuild(#[from] http::Error), } -impl std::error::Error for ResponseRejection {} - -convert_to_response_rejection!(aws_smithy_http::operation::error::BuildError, Build); -convert_to_response_rejection!(aws_smithy_http::operation::error::SerializationError, Serialization); -convert_to_response_rejection!(http::Error, Http); - /// Errors that can occur when deserializing an HTTP request into an _operation input_, the input /// that is passed as the first argument to operation handlers. /// @@ -96,61 +99,72 @@ convert_to_response_rejection!(http::Error, Http); /// deliberate design choice to keep code generation simple. After all, this type is an inner /// detail of the framework the service implementer does not interact with. /// -/// If a variant takes in a value, it represents the underlying cause of the error. This inner -/// value should be of the type-erased boxed error type `[crate::Error]`. In practice, some of the -/// variants that take in a value are only instantiated with errors of a single type in the -/// generated code. For example, `UriPatternMismatch` is only instantiated with an error coming -/// from a `nom` parser, `nom::Err>`. This is reflected in the converters -/// below that convert from one of these very specific error types into one of the variants. For -/// example, the `RequestRejection` implements `From` to construct the `HttpBody` -/// variant. This is a deliberate design choice to make the code simpler and less prone to changes. +/// If a variant takes in a value, it represents the underlying cause of the error. /// /// The variants are _roughly_ sorted in the order in which the HTTP request is processed. -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum RequestRejection { /// Used when failing to convert non-streaming requests into a byte slab with /// `hyper::body::to_bytes`. - HttpBody(crate::Error), + #[error("error converting non-streaming body to bytes: {0}")] + BufferHttpBodyBytes(crate::Error), + + /// Used when the request contained an `Accept` header with a MIME type, and the server cannot + /// return a response body adhering to that MIME type. + #[error("request contains invalid value for `Accept` header")] + NotAcceptable, /// Used when checking the `Content-Type` header. /// This is bubbled up in the generated SDK when calling /// [`crate::protocols::content_type_header_classifier`] in `from_request`. - MissingContentType(MissingContentTypeReason), + #[error("expected `Content-Type` header not found: {0}")] + MissingContentType(#[from] MissingContentTypeReason), /// Used when failing to deserialize the HTTP body's bytes into a JSON document conforming to /// the modeled input it should represent. - JsonDeserialize(crate::Error), + #[error("error deserializing request HTTP body as JSON: {0}")] + JsonDeserialize(#[from] aws_smithy_json::deserialize::error::DeserializeError), /// Used when failing to parse HTTP headers that are bound to input members with the `httpHeader` /// or the `httpPrefixHeaders` traits. - HeaderParse(crate::Error), + #[error("error binding request HTTP headers: {0}")] + HeaderParse(#[from] aws_smithy_http::header::ParseError), + // In theory, the next two errors should never happen because the router should have already + // rejected the request. /// Used when the URI pattern has a literal after the greedy label, and it is not found in the /// request's URL. + #[error("request URI does not match pattern because of literal suffix after greedy label was not found")] UriPatternGreedyLabelPostfixNotFound, /// Used when the `nom` parser's input does not match the URI pattern. + #[error("request URI does not match `@http` URI pattern: {0}")] UriPatternMismatch(crate::Error), /// Used when percent-decoding URL query string. /// Used when percent-decoding URI path label. - InvalidUtf8(crate::Error), + /// This is caused when calling + /// [`percent_encoding::percent_decode_str`](https://docs.rs/percent-encoding/latest/percent_encoding/fn.percent_decode_str.html). + /// This can happen when the percent-encoded data decodes to bytes that are + /// not a well-formed UTF-8 string. + #[error("request URI cannot be percent decoded into valid UTF-8")] + PercentEncodedUriNotValidUtf8(#[from] core::str::Utf8Error), /// Used when failing to deserialize strings from a URL query string and from URI path labels /// into an [`aws_smithy_types::DateTime`]. - DateTimeParse(crate::Error), + #[error("error parsing timestamp from request URI: {0}")] + DateTimeParse(#[from] aws_smithy_types::date_time::DateTimeParseError), /// Used when failing to deserialize strings from a URL query string and from URI path labels /// into "primitive" types. - PrimitiveParse(crate::Error), + #[error("error parsing primitive type from request URI: {0}")] + PrimitiveParse(#[from] aws_smithy_types::primitive::PrimitiveParseError), /// Used when consuming the input struct builder, and constraint violations occur. - // Unlike the rejections above, this does not take in `crate::Error`, since it is constructed - // directly in the code-generated SDK instead of in this crate. + // This rejection is constructed directly in the code-generated SDK instead of in this crate. + #[error("request does not adhere to modeled constraints: {0}")] ConstraintViolation(String), } -impl std::error::Error for RequestRejection {} - // Consider a conversion between `T` and `U` followed by a bubbling up of the conversion error // through `Result<_, RequestRejection>`. This [`From`] implementation accomodates the special case // where `T` and `U` are equal, in such cases `T`/`U` a enjoy `TryFrom` with @@ -170,43 +184,24 @@ impl From for RequestRejection { } } -impl From for RequestRejection { - fn from(e: MissingContentTypeReason) -> Self { - Self::MissingContentType(e) - } -} - // These converters are solely to make code-generation simpler. They convert from a specific error // type (from a runtime/third-party crate or the standard library) into a variant of the // [`crate::rejection::RequestRejection`] enum holding the type-erased boxed [`crate::Error`] // type. Generated functions that use [crate::rejection::RequestRejection] can thus use `?` to // bubble up instead of having to sprinkle things like [`Result::map_err`] everywhere. -convert_to_request_rejection!(aws_smithy_json::deserialize::error::DeserializeError, JsonDeserialize); -convert_to_request_rejection!(aws_smithy_http::header::ParseError, HeaderParse); -convert_to_request_rejection!(aws_smithy_types::date_time::DateTimeParseError, DateTimeParse); -convert_to_request_rejection!(aws_smithy_types::primitive::PrimitiveParseError, PrimitiveParse); -convert_to_request_rejection!(serde_urlencoded::de::Error, InvalidUtf8); - impl From>> for RequestRejection { fn from(err: nom::Err>) -> Self { Self::UriPatternMismatch(crate::Error::new(err.to_owned())) } } -// Used when calling -// [`percent_encoding::percent_decode_str`](https://docs.rs/percent-encoding/latest/percent_encoding/fn.percent_decode_str.html) -// and bubbling up. -// This can happen when the percent-encoded data in e.g. a query string decodes to bytes that are -// not a well-formed UTF-8 string. -convert_to_request_rejection!(std::str::Utf8Error, InvalidUtf8); - // `[crate::body::Body]` is `[hyper::Body]`, whose associated `Error` type is `[hyper::Error]`. We // need this converter for when we convert the body into bytes in the framework, since protocol // tests use `[crate::body::Body]` as their body type when constructing requests (and almost // everyone will run a Hyper-based server in their services). -convert_to_request_rejection!(hyper::Error, HttpBody); +convert_to_request_rejection!(hyper::Error, BufferHttpBodyBytes); // Useful in general, but it also required in order to accept Lambda HTTP requests using // `Router` since `lambda_http::Error` is a type alias for `Box`. -convert_to_request_rejection!(Box, HttpBody); +convert_to_request_rejection!(Box, BufferHttpBodyBytes); diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs similarity index 93% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs index 3a2a5111b6..76d3d69277 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs @@ -5,13 +5,12 @@ use crate::body::BoxBody; use crate::extension::RuntimeErrorExtension; -use crate::proto::rest::router::Error; use crate::response::IntoResponse; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::RestJson1; -pub use crate::proto::rest::router::*; +pub use crate::protocol::rest::router::*; impl IntoResponse for Error { fn into_response(self) -> http::Response { diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs similarity index 91% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs index 3cf49687ca..1518c9eeeb 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs @@ -7,27 +7,27 @@ //! //! This module contains the [`RuntimeError`] type. //! -//! As opposed to rejection types (see [`crate::proto::rest_json_1::rejection`]), which are an internal detail about +//! As opposed to rejection types (see [`crate::protocol::rest_json_1::rejection`]), which are an internal detail about //! the framework, `RuntimeError` is surfaced to clients in HTTP responses: indeed, it implements //! [`RuntimeError::into_response`]. Rejections can be "grouped" and converted into a //! specific `RuntimeError` kind: for example, all request rejections due to serialization issues //! can be conflated under the [`RuntimeError::Serialization`] enum variant. //! //! The HTTP response representation of the specific `RuntimeError` is protocol-specific: for -//! example, the runtime error in the [`crate::proto::rest_json_1`] protocol sets the `X-Amzn-Errortype` header. +//! example, the runtime error in the [`crate::protocol::rest_json_1`] protocol sets the `X-Amzn-Errortype` header. //! //! Generated code works always works with [`crate::rejection`] types when deserializing requests //! and serializing response. Just before a response needs to be sent, the generated code looks up //! and converts into the corresponding `RuntimeError`, and then it uses the its //! [`RuntimeError::into_response`] method to render and send a response. //! -//! This module hosts the `RuntimeError` type _specific_ to the [`crate::proto::rest_json_1`] protocol, but +//! This module hosts the `RuntimeError` type _specific_ to the [`crate::protocol::rest_json_1`] protocol, but //! the paragraphs above apply to _all_ protocol-specific rejection types. //! //! Similarly, `RuntimeError` variants are exhaustively documented solely in this module if they have //! direct counterparts in other protocols. This is to avoid documentation getting out of date. //! -//! Consult `crate::proto::$protocolName::runtime_error` for the `RuntimeError` type for other protocols. +//! Consult `crate::protocol::$protocolName::runtime_error` for the `RuntimeError` type for other protocols. use super::rejection::RequestRejection; use super::rejection::ResponseRejection; @@ -118,6 +118,7 @@ impl From for RuntimeError { match err { RequestRejection::MissingContentType(_reason) => Self::UnsupportedMediaType, RequestRejection::ConstraintViolation(reason) => Self::Validation(reason), + RequestRejection::NotAcceptable => Self::NotAcceptable, _ => Self::Serialization(crate::Error::new(err)), } } diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_xml/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/rejection.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/rejection.rs new file mode 100644 index 0000000000..3e1bed00ca --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/rejection.rs @@ -0,0 +1,76 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! This module hosts _exactly_ the same as [`crate::protocol::rest_json_1::rejection`], except that +//! [`crate::protocol::rest_json_1::rejection::RequestRejection::JsonDeserialize`] is swapped for +//! [`RequestRejection::XmlDeserialize`]. + +use crate::rejection::MissingContentTypeReason; +use std::num::TryFromIntError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum ResponseRejection { + #[error("invalid bound HTTP status code; status codes must be inside the 100-999 range: {0}")] + InvalidHttpStatusCode(TryFromIntError), + #[error("error building HTTP response: {0}")] + Build(#[from] aws_smithy_http::operation::error::BuildError), + #[error("error serializing XML-encoded body: {0}")] + Serialization(#[from] aws_smithy_http::operation::error::SerializationError), + #[error("error building HTTP response: {0}")] + HttpBuild(#[from] http::Error), +} + +#[derive(Debug, Error)] +pub enum RequestRejection { + #[error("error converting non-streaming body to bytes: {0}")] + BufferHttpBodyBytes(crate::Error), + + #[error("request contains invalid value for `Accept` header")] + NotAcceptable, + + #[error("expected `Content-Type` header not found: {0}")] + MissingContentType(#[from] MissingContentTypeReason), + + /// Used when failing to deserialize the HTTP body's bytes into a XML conforming to the modeled + /// input it should represent. + #[error("error deserializing request HTTP body as XML: {0}")] + XmlDeserialize(#[from] aws_smithy_xml::decode::XmlDecodeError), + + #[error("error binding request HTTP headers: {0}")] + HeaderParse(#[from] aws_smithy_http::header::ParseError), + + #[error("request URI does not match pattern because of literal suffix after greedy label was not found")] + UriPatternGreedyLabelPostfixNotFound, + #[error("request URI does not match `@http` URI pattern: {0}")] + UriPatternMismatch(crate::Error), + + #[error("request URI cannot be percent decoded into valid UTF-8")] + PercentEncodedUriNotValidUtf8(#[from] core::str::Utf8Error), + + #[error("error parsing timestamp from request URI: {0}")] + DateTimeParse(#[from] aws_smithy_types::date_time::DateTimeParseError), + + #[error("error parsing primitive type from request URI: {0}")] + PrimitiveParse(#[from] aws_smithy_types::primitive::PrimitiveParseError), + + #[error("request does not adhere to modeled constraints: {0}")] + ConstraintViolation(String), +} + +impl From for RequestRejection { + fn from(_err: std::convert::Infallible) -> Self { + match _err {} + } +} + +impl From>> for RequestRejection { + fn from(err: nom::Err>) -> Self { + Self::UriPatternMismatch(crate::Error::new(err.to_owned())) + } +} + +convert_to_request_rejection!(hyper::Error, BufferHttpBodyBytes); +convert_to_request_rejection!(Box, BufferHttpBodyBytes); diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs similarity index 93% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs index b771884b04..d8a0905a7f 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs @@ -6,13 +6,12 @@ use crate::body::empty; use crate::body::BoxBody; use crate::extension::RuntimeErrorExtension; -use crate::proto::rest::router::Error; use crate::response::IntoResponse; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::RestXml; -pub use crate::proto::rest::router::*; +pub use crate::protocol::rest::router::*; /// An AWS REST routing error. impl IntoResponse for Error { diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs similarity index 98% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_xml/runtime_error.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs index 9a7a89e823..d4ecb5c218 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::proto::rest_xml::RestXml; +use crate::protocol::rest_xml::RestXml; use crate::response::IntoResponse; use crate::runtime_error::InternalFailureException; use crate::{extension::RuntimeErrorExtension, runtime_error::INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE}; diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index 209a6721f3..1f1e247435 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -3,17 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -use strum_macros::Display; - use crate::response::IntoResponse; +use thiserror::Error; // This is used across different protocol-specific `rejection` modules. -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum MissingContentTypeReason { + #[error("headers taken by another extractor")] HeadersTakenByAnotherExtractor, + #[error("no `Content-Type` header")] NoContentTypeHeader, + #[error("`Content-Type` header value is not a valid HTTP header value: {0}")] ToStrError(http::header::ToStrError), + #[error("invalid `Content-Type` header value mime type: {0}")] MimeParseError(mime::FromStrError), + #[error("unexpected `Content-Type` header value; expected {expected_mime:?}, found {found_mime:?}")] UnexpectedMimeType { expected_mime: Option, found_mime: Option, diff --git a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs index b338062782..44704e2dd1 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs @@ -16,6 +16,10 @@ type HyperRequest = http::Request; /// A [`Service`] that takes a `lambda_http::Request` and converts /// it to `http::Request`. /// +/// **This version is only guaranteed to be compatible with +/// [`lambda_http`](https://docs.rs/lambda_http) ^0.7.0.** Please ensure that your service crate's +/// `Cargo.toml` depends on a compatible version. +/// /// [`Service`]: tower::Service #[derive(Debug, Clone)] pub struct LambdaHandler { @@ -54,12 +58,12 @@ where /// /// [API Gateway Stage]: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-stages.html fn convert_event(request: Request) -> HyperRequest { - let raw_path = request.raw_http_path(); - let (mut parts, body) = request.into_parts(); - let mut path = String::from(parts.uri.path()); + let raw_path: &str = request.extensions().raw_http_path(); + let path: &str = request.uri().path(); - if !raw_path.is_empty() && raw_path != path { - path = raw_path; + let (parts, body) = if !raw_path.is_empty() && raw_path != path { + let mut path = raw_path.to_owned(); // Clone only when we need to strip out the stage. + let (mut parts, body) = request.into_parts(); let uri_parts: uri::Parts = parts.uri.into(); let path_and_query = uri_parts @@ -77,7 +81,11 @@ fn convert_event(request: Request) -> HyperRequest { .path_and_query(path) .build() .expect("unable to construct new URI"); - } + + (parts, body) + } else { + request.into_parts() + }; let body = match body { lambda_http::Body::Empty => hyper::Body::empty(), diff --git a/rust-runtime/aws-smithy-http-server/src/routing/request_spec.rs b/rust-runtime/aws-smithy-http-server/src/routing/request_spec.rs index 5ed493360b..3ae722f5fd 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/request_spec.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/request_spec.rs @@ -250,7 +250,7 @@ impl RequestSpec { #[cfg(test)] mod tests { use super::*; - use crate::proto::test_helpers::req; + use crate::protocol::test_helpers::req; use http::Method; diff --git a/rust-runtime/aws-smithy-http-server/src/routing/tiny_map.rs b/rust-runtime/aws-smithy-http-server/src/routing/tiny_map.rs index 236da27797..7011515641 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/tiny_map.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/tiny_map.rs @@ -78,13 +78,13 @@ where // Populate the `Vec` while let Some((index, pair)) = iter.next() { + vec.push(pair); + // If overflow `CUTOFF` then return a `HashMap` instead if index == CUTOFF { let inner = TinyMapInner::HashMap(vec.into_iter().chain(iter.map(|(_, pair)| pair)).collect()); return TinyMap { inner }; } - - vec.push(pair); } TinyMap { @@ -158,19 +158,25 @@ mod tests { #[test] fn get_small_success() { let tiny_map: TinyMap<_, _, CUTOFF> = SMALL_VALUES.into_iter().collect(); - assert_eq!(tiny_map.get("a"), Some(&0)) + SMALL_VALUES.into_iter().for_each(|(op, val)| { + assert_eq!(tiny_map.get(op), Some(&val)); + }); } #[test] fn get_medium_success() { let tiny_map: TinyMap<_, _, CUTOFF> = MEDIUM_VALUES.into_iter().collect(); - assert_eq!(tiny_map.get("d"), Some(&3)) + MEDIUM_VALUES.into_iter().for_each(|(op, val)| { + assert_eq!(tiny_map.get(op), Some(&val)); + }); } #[test] fn get_large_success() { let tiny_map: TinyMap<_, _, CUTOFF> = LARGE_VALUES.into_iter().collect(); - assert_eq!(tiny_map.get("h"), Some(&7)) + LARGE_VALUES.into_iter().for_each(|(op, val)| { + assert_eq!(tiny_map.get(op), Some(&val)); + }); } #[test] diff --git a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs index 97dca6ad19..7348f5d628 100644 --- a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs @@ -6,8 +6,8 @@ /// A _protocol-agnostic_ type representing an internal framework error. As of writing, this can only /// occur upon failure to extract an [`crate::extension::Extension`] from the request. /// This type is converted into protocol-specific error variants. For example, in the -/// [`crate::proto::rest_json_1`] protocol, it is converted to the -/// [`crate::proto::rest_json_1::runtime_error::RuntimeError::InternalFailure`] variant. +/// [`crate::protocol::rest_json_1`] protocol, it is converted to the +/// [`crate::protocol::rest_json_1::runtime_error::RuntimeError::InternalFailure`] variant. pub struct InternalFailureException; pub const INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE: &str = "invalid HTTP response for `RuntimeError`; please file a bug report under https://github.com/awslabs/smithy-rs/issues"; diff --git a/rust-runtime/aws-smithy-http-server/src/service.rs b/rust-runtime/aws-smithy-http-server/src/service.rs new file mode 100644 index 0000000000..9b0537490f --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/service.rs @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! The shape of a [Smithy service] is modelled by the [`ServiceShape`] trait. Its associated types +//! [`ServiceShape::ID`], [`ServiceShape::VERSION`], [`ServiceShape::Protocol`], and [`ServiceShape::Operations`] map +//! to the services [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id), the version field, the applied +//! [protocol trait](https://smithy.io/2.0/aws/protocols/index.html) (see [`protocol`](crate::protocol) module), and the +//! operations field. +//! +//! We generate an implementation on this for every service struct (exported from the root of the generated crate). +//! +//! As stated in the [operation module documentation](crate::operation) we also generate marker structs for +//! [`OperationShape`](crate::operation::OperationShape), these are coupled to the `S: ServiceShape` via the [`ContainsOperation`] trait. +//! +//! The model +//! +//! ```smithy +//! @restJson1 +//! service Shopping { +//! version: "1.0", +//! operations: [ +//! GetShopping, +//! PutShopping +//! ] +//! } +//! ``` +//! +//! is identified with the implementation +//! +//! ```rust,no_run +//! # use aws_smithy_http_server::shape_id::ShapeId; +//! # use aws_smithy_http_server::service::{ServiceShape, ContainsOperation}; +//! # use aws_smithy_http_server::protocol::rest_json_1::RestJson1; +//! # pub struct Shopping; +//! // For more information on these marker structs see `OperationShape` +//! struct GetShopping; +//! struct PutShopping; +//! +//! // This is a generated enumeration of all operations. +//! #[derive(PartialEq, Eq, Clone, Copy)] +//! pub enum Operation { +//! GetShopping, +//! PutShopping +//! } +//! +//! impl ServiceShape for Shopping { +//! const ID: ShapeId = ShapeId::new("namespace#Shopping", "namespace", "Shopping"); +//! const VERSION: Option<&'static str> = Some("1.0"); +//! type Protocol = RestJson1; +//! type Operations = Operation; +//! } +//! +//! impl ContainsOperation for Shopping { +//! const VALUE: Operation = Operation::GetShopping; +//! } +//! +//! impl ContainsOperation for Shopping { +//! const VALUE: Operation = Operation::PutShopping; +//! } +//! ``` +//! +//! [Smithy service]: https://smithy.io/2.0/spec/service-types.html#service + +use crate::shape_id::ShapeId; + +/// Models the [Smithy Service shape]. +/// +/// [Smithy Service shape]: https://smithy.io/2.0/spec/service-types.html#service +pub trait ServiceShape { + /// The [`ShapeId`] of the service. + const ID: ShapeId; + + /// The version of the service. + const VERSION: Option<&'static str>; + + /// The [Protocol] applied to this service. + /// + /// [Protocol]: https://smithy.io/2.0/spec/protocol-traits.html + type Protocol; + + /// An enumeration of all operations contained in this service. + type Operations; +} + +pub trait ContainsOperation: ServiceShape { + const VALUE: Self::Operations; +} diff --git a/rust-runtime/aws-smithy-http-server/src/shape_id.rs b/rust-runtime/aws-smithy-http-server/src/shape_id.rs new file mode 100644 index 0000000000..cdc73c8412 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/shape_id.rs @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! A [`ShapeId`] represents a [Smithy Shape ID](https://smithy.io/2.0/spec/model.html#shape-id). +//! +//! # Example +//! +//! In the following model: +//! +//! ```smithy +//! namespace smithy.example +//! +//! operation CheckHealth {} +//! ``` +//! +//! - `absolute` is `"smithy.example#CheckHealth"` +//! - `namespace` is `"smithy.example"` +//! - `name` is `"CheckHealth"` + +pub use crate::request::extension::{Extension, MissingExtension}; + +/// Represents a [Smithy Shape ID](https://smithy.io/2.0/spec/model.html#shape-id). +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ShapeId { + absolute: &'static str, + + namespace: &'static str, + name: &'static str, +} + +impl ShapeId { + /// Constructs a new [`ShapeId`]. This is used by the code-generator which preserves the invariants of the Shape ID format. + #[doc(hidden)] + pub const fn new(absolute: &'static str, namespace: &'static str, name: &'static str) -> Self { + Self { + absolute, + namespace, + name, + } + } + + /// Returns the namespace. + /// + /// See [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id) for a breakdown of the syntax. + pub fn namespace(&self) -> &'static str { + self.namespace + } + + /// Returns the member name. + /// + /// See [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id) for a breakdown of the syntax. + pub fn name(&self) -> &'static str { + self.name + } + + /// Returns the absolute shape ID. + /// + /// See [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id) for a breakdown of the syntax. + pub fn absolute(&self) -> &'static str { + self.absolute + } +} diff --git a/rust-runtime/aws-smithy-http/Cargo.toml b/rust-runtime/aws-smithy-http/Cargo.toml index e26419e4df..0238f5dfc6 100644 --- a/rust-runtime/aws-smithy-http/Cargo.toml +++ b/rust-runtime/aws-smithy-http/Cargo.toml @@ -28,7 +28,7 @@ pin-utils = "0.1.0" tracing = "0.1" # We are using hyper for our streaming body implementation, but this is an internal detail. -hyper = "0.14.25" +hyper = "0.14.26" # ByteStream internals futures-core = "0.3.14" @@ -38,7 +38,7 @@ tokio-util = { version = "0.7", optional = true } [dev-dependencies] async-stream = "0.3" futures-util = { version = "0.3.16", default-features = false } -hyper = { version = "0.14.25", features = ["stream"] } +hyper = { version = "0.14.26", features = ["stream"] } pretty_assertions = "1.3" proptest = "1" tokio = { version = "1.23.1", features = [ diff --git a/rust-runtime/aws-smithy-http/external-types.toml b/rust-runtime/aws-smithy-http/external-types.toml index b06231e92f..a228978c9a 100644 --- a/rust-runtime/aws-smithy-http/external-types.toml +++ b/rust-runtime/aws-smithy-http/external-types.toml @@ -31,7 +31,4 @@ allowed_external_types = [ # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Once tooling permits it, only allow the following types in the `event-stream` feature "aws_smithy_eventstream::*", - - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Decide whether to expose this type or not - "bytes_utils::segmented::SegmentedBuf", ] diff --git a/rust-runtime/aws-smithy-http/src/byte_stream.rs b/rust-runtime/aws-smithy-http/src/byte_stream.rs index fcd697c7b4..e067018a9d 100644 --- a/rust-runtime/aws-smithy-http/src/byte_stream.rs +++ b/rust-runtime/aws-smithy-http/src/byte_stream.rs @@ -484,11 +484,7 @@ impl AggregatedBytes { /// Convert this buffer into a `Vec` pub fn to_vec(self) -> Vec { - self.0 - .into_inner() - .into_iter() - .flat_map(|b| b.to_vec()) - .collect() + self.0.into_inner().into_iter().flatten().collect() } } diff --git a/rust-runtime/aws-smithy-http/src/connection.rs b/rust-runtime/aws-smithy-http/src/connection.rs index eb81f63687..f98bb780bd 100644 --- a/rust-runtime/aws-smithy-http/src/connection.rs +++ b/rust-runtime/aws-smithy-http/src/connection.rs @@ -82,7 +82,7 @@ impl CaptureSmithyConnection { match self.loader.lock().unwrap().as_ref() { Some(loader) => loader(), None => { - println!("no loader was set :-/"); + tracing::debug!("no loader was set on the CaptureSmithyConnection"); None } } diff --git a/rust-runtime/aws-smithy-http/src/endpoint.rs b/rust-runtime/aws-smithy-http/src/endpoint.rs index e73cd05131..3a4939434e 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint.rs @@ -7,10 +7,13 @@ use crate::endpoint::error::InvalidEndpointError; use crate::operation::error::BuildError; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use http::uri::{Authority, Uri}; use std::borrow::Cow; +use std::fmt::{Debug, Formatter}; use std::result::Result as StdResult; use std::str::FromStr; +use std::sync::Arc; pub mod error; pub mod middleware; @@ -35,6 +38,45 @@ impl ResolveEndpoint for &'static str { } } +/// Endpoint Resolver wrapper that may be shared +#[derive(Clone)] +pub struct SharedEndpointResolver(Arc>); + +impl Debug for SharedEndpointResolver { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SharedEndpointResolver").finish() + } +} + +impl SharedEndpointResolver { + /// Create a new `SharedEndpointResolver` from `ResolveEndpoint` + pub fn new(resolve_endpoint: impl ResolveEndpoint + 'static) -> Self { + Self(Arc::new(resolve_endpoint)) + } +} + +impl AsRef> for SharedEndpointResolver { + fn as_ref(&self) -> &(dyn ResolveEndpoint + 'static) { + self.0.as_ref() + } +} + +impl From>> for SharedEndpointResolver { + fn from(resolve_endpoint: Arc>) -> Self { + SharedEndpointResolver(resolve_endpoint) + } +} + +impl Storable for SharedEndpointResolver { + type Storer = StoreReplace>; +} + +impl ResolveEndpoint for SharedEndpointResolver { + fn resolve_endpoint(&self, params: &T) -> Result { + self.0.resolve_endpoint(params) + } +} + /// API Endpoint /// /// This implements an API endpoint as specified in the @@ -82,6 +124,10 @@ impl EndpointPrefix { } } +impl Storable for EndpointPrefix { + type Storer = StoreReplace; +} + /// Apply `endpoint` to `uri` /// /// This method mutates `uri` by setting the `endpoint` on it diff --git a/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs b/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs index 1665df9bce..1fbb099b11 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs @@ -13,6 +13,8 @@ use http::header::HeaderName; use http::{HeaderValue, Uri}; use std::str::FromStr; +// TODO(enableNewSmithyRuntimeCleanup): Delete this module + /// Middleware to apply an HTTP endpoint to the request /// /// This middleware reads [`aws_smithy_types::endpoint::Endpoint`] out of the request properties and applies diff --git a/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs b/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs index aa7f914223..3b38161f3b 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs @@ -97,8 +97,8 @@ pub enum RawMessage { Invalid(Option), } -impl From<&mut SegmentedBuf> for RawMessage { - fn from(buf: &mut SegmentedBuf) -> Self { +impl RawMessage { + pub(crate) fn invalid(buf: &mut SegmentedBuf) -> Self { Self::Invalid(Some(buf.copy_to_bytes(buf.remaining()))) } } @@ -213,7 +213,7 @@ impl Receiver { ReceiverError { kind: ReceiverErrorKind::UnexpectedEndOfStream, }, - self.buffer.buffered().into(), + RawMessage::invalid(self.buffer.buffered()), )); } Ok(None) diff --git a/rust-runtime/aws-smithy-http/src/event_stream/sender.rs b/rust-runtime/aws-smithy-http/src/event_stream/sender.rs index a4faa3f236..d19690e727 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream/sender.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream/sender.rs @@ -17,12 +17,14 @@ use tracing::trace; /// Input type for Event Streams. pub struct EventStreamSender { - input_stream: Pin> + Send>>, + input_stream: Pin> + Send + Sync>>, } impl Debug for EventStreamSender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "EventStreamSender(Box)") + let name_t = std::any::type_name::(); + let name_e = std::any::type_name::(); + write!(f, "EventStreamSender<{name_t}, {name_e}>") } } @@ -40,7 +42,7 @@ impl EventStreamSender { impl From for EventStreamSender where - S: Stream> + Send + 'static, + S: Stream> + Send + Sync + 'static, { fn from(stream: S) -> Self { EventStreamSender { @@ -260,6 +262,17 @@ mod tests { } } + fn check_send_sync(value: T) -> T { + value + } + + #[test] + fn event_stream_sender_send_sync() { + check_send_sync(EventStreamSender::from(stream! { + yield Result::<_, SignMessageError>::Ok(TestMessage("test".into())); + })); + } + fn check_compatible_with_hyper_wrap_stream(stream: S) -> S where S: Stream> + Send + 'static, diff --git a/rust-runtime/aws-smithy-http/src/middleware.rs b/rust-runtime/aws-smithy-http/src/middleware.rs index b77c65b935..3285dec872 100644 --- a/rust-runtime/aws-smithy-http/src/middleware.rs +++ b/rust-runtime/aws-smithy-http/src/middleware.rs @@ -21,6 +21,8 @@ use tracing::{debug_span, trace, Instrument}; type BoxError = Box; +const LOG_SENSITIVE_BODIES: &str = "LOG_SENSITIVE_BODIES"; + /// [`AsyncMapRequest`] defines an asynchronous middleware that transforms an [`operation::Request`]. /// /// Typically, these middleware will read configuration from the `PropertyBag` and use it to @@ -110,7 +112,7 @@ where O: ParseHttpResponse>, { if let Some(parsed_response) = - debug_span!("parse_unloaded").in_scope(&mut || handler.parse_unloaded(&mut response)) + debug_span!("parse_unloaded").in_scope(|| handler.parse_unloaded(&mut response)) { trace!(response = ?response, "read HTTP headers for streaming response"); return sdk_result(parsed_response, response); @@ -132,7 +134,15 @@ where }; let http_response = http::Response::from_parts(parts, Bytes::from(body)); - trace!(http_response = ?http_response, "read HTTP response body"); + if !handler.sensitive() + || std::env::var(LOG_SENSITIVE_BODIES) + .map(|v| v.eq_ignore_ascii_case("true")) + .unwrap_or_default() + { + trace!(http_response = ?http_response, "read HTTP response body"); + } else { + trace!(http_response = "** REDACTED **. To print, set LOG_SENSITIVE_BODIES=true") + } debug_span!("parse_loaded").in_scope(move || { let parsed = handler.parse_loaded(&http_response); sdk_result( diff --git a/rust-runtime/aws-smithy-http/src/operation.rs b/rust-runtime/aws-smithy-http/src/operation.rs index a4d9657cfb..2ddbd28070 100644 --- a/rust-runtime/aws-smithy-http/src/operation.rs +++ b/rust-runtime/aws-smithy-http/src/operation.rs @@ -9,6 +9,7 @@ use crate::body::SdkBody; use crate::property_bag::{PropertyBag, SharedPropertyBag}; use crate::retry::DefaultResponseRetryClassifier; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::borrow::Cow; use std::ops::{Deref, DerefMut}; @@ -44,6 +45,10 @@ impl Metadata { } } +impl Storable for Metadata { + type Storer = StoreReplace; +} + /// Non-request parts of an [`Operation`]. /// /// Generics: @@ -60,6 +65,7 @@ pub struct Parts { pub metadata: Option, } +// TODO(enableNewSmithyRuntimeCleanup): Delete `operation::Operation` when cleaning up middleware /// An [`Operation`] is a request paired with a response handler, retry classifier, /// and metadata that identifies the API being called. /// @@ -159,6 +165,7 @@ impl Operation { } } +// TODO(enableNewSmithyRuntimeCleanup): Delete `operation::Request` when cleaning up middleware /// Operation request type that associates a property bag with an underlying HTTP request. /// This type represents the request in the Tower `Service` in middleware so that middleware /// can share information with each other via the properties. @@ -250,6 +257,7 @@ impl Request { } } +// TODO(enableNewSmithyRuntimeCleanup): Delete `operation::Response` when cleaning up middleware /// Operation response type that associates a property bag with an underlying HTTP response. /// This type represents the response in the Tower `Service` in middleware so that middleware /// can share information with each other via the properties. diff --git a/rust-runtime/aws-smithy-http/src/property_bag.rs b/rust-runtime/aws-smithy-http/src/property_bag.rs index 4964b7f09b..1ac6adc989 100644 --- a/rust-runtime/aws-smithy-http/src/property_bag.rs +++ b/rust-runtime/aws-smithy-http/src/property_bag.rs @@ -13,12 +13,38 @@ use std::any::{Any, TypeId}; use std::collections::HashMap; use std::fmt; -use std::fmt::Debug; +use std::fmt::{Debug, Formatter}; use std::hash::{BuildHasherDefault, Hasher}; use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Mutex}; -type AnyMap = HashMap, BuildHasherDefault>; +type AnyMap = HashMap>; + +struct NamedType { + name: &'static str, + value: Box, +} + +impl NamedType { + fn as_mut(&mut self) -> Option<&mut T> { + self.value.downcast_mut() + } + + fn as_ref(&self) -> Option<&T> { + self.value.downcast_ref() + } + + fn assume(self) -> Option { + self.value.downcast().map(|t| *t).ok() + } + + fn new(value: T) -> Self { + Self { + name: std::any::type_name::(), + value: Box::new(value), + } + } +} // With TypeIds as keys, there's no need to hash them. They are already hashes // themselves, coming from the compiler. The IdHasher just holds the u64 of @@ -82,13 +108,8 @@ impl PropertyBag { /// ``` pub fn insert(&mut self, val: T) -> Option { self.map - .insert(TypeId::of::(), Box::new(val)) - .and_then(|boxed| { - (boxed as Box) - .downcast() - .ok() - .map(|boxed| *boxed) - }) + .insert(TypeId::of::(), NamedType::new(val)) + .and_then(|val| val.assume()) } /// Get a reference to a type previously inserted on this `PropertyBag`. @@ -106,7 +127,16 @@ impl PropertyBag { pub fn get(&self) -> Option<&T> { self.map .get(&TypeId::of::()) - .and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref()) + .and_then(|val| val.as_ref()) + } + + /// Returns an iterator of the types contained in this PropertyBag + /// + /// # Stability + /// This method is unstable and may be removed or changed in a future release. The exact + /// format of the returned types may also change. + pub fn contents(&self) -> impl Iterator + '_ { + self.map.values().map(|tpe| tpe.name) } /// Get a mutable reference to a type previously inserted on this `PropertyBag`. @@ -124,7 +154,7 @@ impl PropertyBag { pub fn get_mut(&mut self) -> Option<&mut T> { self.map .get_mut(&TypeId::of::()) - .and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut()) + .map(|val| val.as_mut().expect("type mismatch!")) } /// Remove a type from this `PropertyBag`. @@ -141,8 +171,8 @@ impl PropertyBag { /// assert!(props.get::().is_none()); /// ``` pub fn remove(&mut self) -> Option { - self.map.remove(&TypeId::of::()).and_then(|boxed| { - (boxed as Box) + self.map.remove(&TypeId::of::()).and_then(|tpe| { + (tpe.value as Box) .downcast() .ok() .map(|boxed| *boxed) @@ -168,7 +198,16 @@ impl PropertyBag { impl fmt::Debug for PropertyBag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PropertyBag").finish() + let mut fmt = f.debug_struct("PropertyBag"); + + struct Contents<'a>(&'a PropertyBag); + impl<'a> Debug for Contents<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.contents()).finish() + } + } + fmt.field("contents", &Contents(self)); + fmt.finish() } } @@ -225,22 +264,30 @@ impl From for SharedPropertyBag { } #[cfg(test)] -#[test] -fn test_extensions() { - #[derive(Debug, PartialEq)] - struct MyType(i32); +mod test { + use crate::property_bag::PropertyBag; - let mut extensions = PropertyBag::new(); + #[test] + fn test_extensions() { + #[derive(Debug, PartialEq)] + struct MyType(i32); - extensions.insert(5i32); - extensions.insert(MyType(10)); + let mut property_bag = PropertyBag::new(); - assert_eq!(extensions.get(), Some(&5i32)); - assert_eq!(extensions.get_mut(), Some(&mut 5i32)); + property_bag.insert(5i32); + property_bag.insert(MyType(10)); - assert_eq!(extensions.remove::(), Some(5i32)); - assert!(extensions.get::().is_none()); + assert_eq!(property_bag.get(), Some(&5i32)); + assert_eq!(property_bag.get_mut(), Some(&mut 5i32)); - assert_eq!(extensions.get::(), None); - assert_eq!(extensions.get(), Some(&MyType(10))); + assert_eq!(property_bag.remove::(), Some(5i32)); + assert!(property_bag.get::().is_none()); + + assert_eq!(property_bag.get::(), None); + assert_eq!(property_bag.get(), Some(&MyType(10))); + assert_eq!( + format!("{:?}", property_bag), + r#"PropertyBag { contents: ["aws_smithy_http::property_bag::test::test_extensions::MyType"] }"# + ); + } } diff --git a/rust-runtime/aws-smithy-http/src/response.rs b/rust-runtime/aws-smithy-http/src/response.rs index 9102fcbab4..7ba039f419 100644 --- a/rust-runtime/aws-smithy-http/src/response.rs +++ b/rust-runtime/aws-smithy-http/src/response.rs @@ -60,6 +60,13 @@ pub trait ParseHttpResponse { /// if `parse_unloaded` will never return `None`, however, it may make your code easier to test if an /// implementation is provided. fn parse_loaded(&self, response: &http::Response) -> Self::Output; + + /// Returns whether the contents of this response are sensitive + /// + /// When this is set to true, wire logging will be disabled + fn sensitive(&self) -> bool { + false + } } /// Convenience Trait for non-streaming APIs @@ -72,6 +79,13 @@ pub trait ParseStrictResponse { /// Parse an [`http::Response`] into `Self::Output`. fn parse(&self, response: &http::Response) -> Self::Output; + + /// Returns whether the contents of this response are sensitive + /// + /// When this is set to true, wire logging will be disabled + fn sensitive(&self) -> bool { + false + } } impl ParseHttpResponse for T { @@ -84,6 +98,10 @@ impl ParseHttpResponse for T { fn parse_loaded(&self, response: &http::Response) -> Self::Output { self.parse(response) } + + fn sensitive(&self) -> bool { + ParseStrictResponse::sensitive(self) + } } #[cfg(test)] diff --git a/rust-runtime/aws-smithy-http/src/result.rs b/rust-runtime/aws-smithy-http/src/result.rs index 0ce42c7cbd..8d60c8d2ed 100644 --- a/rust-runtime/aws-smithy-http/src/result.rs +++ b/rust-runtime/aws-smithy-http/src/result.rs @@ -5,15 +5,17 @@ //! `Result` wrapper types for [success](SdkSuccess) and [failure](SdkError) responses. -use crate::connection::ConnectionMetadata; -use crate::operation; -use aws_smithy_types::error::metadata::{ProvideErrorMetadata, EMPTY_ERROR_METADATA}; -use aws_smithy_types::error::ErrorMetadata; -use aws_smithy_types::retry::ErrorKind; use std::error::Error; use std::fmt; use std::fmt::{Debug, Display, Formatter}; +use aws_smithy_types::error::metadata::{ProvideErrorMetadata, EMPTY_ERROR_METADATA}; +use aws_smithy_types::error::ErrorMetadata; +use aws_smithy_types::retry::ErrorKind; + +use crate::connection::ConnectionMetadata; +use crate::operation; + type BoxError = Box; /// Successful SDK Result @@ -230,11 +232,16 @@ impl DispatchFailure { self.source.is_user() } - /// Returns the optional error kind associated with an unclassified error - pub fn is_other(&self) -> Option { + /// Returns true if the error is an unclassified error. + pub fn is_other(&self) -> bool { self.source.is_other() } + /// Returns the optional error kind associated with an unclassified error + pub fn as_other(&self) -> Option { + self.source.as_other() + } + /// Returns the inner error if it is a connector error pub fn as_connector_error(&self) -> Option<&ConnectorError> { Some(&self.source) @@ -434,6 +441,15 @@ impl SdkError { } } + /// Return a reference to this error's raw response, if it contains one. Otherwise, return `None`. + pub fn raw_response(&self) -> Option<&R> { + match self { + Self::ServiceError(inner) => Some(inner.raw()), + Self::ResponseError(inner) => Some(inner.raw()), + _ => None, + } + } + /// Maps the service error type in `SdkError::ServiceError` #[doc(hidden)] pub fn map_service_error(self, map: impl FnOnce(E) -> E2) -> SdkError { @@ -622,8 +638,13 @@ impl ConnectorError { matches!(self.kind, ConnectorErrorKind::User) } + /// Returns true if the error is an unclassified error. + pub fn is_other(&self) -> bool { + matches!(self.kind, ConnectorErrorKind::Other(..)) + } + /// Returns the optional error kind associated with an unclassified error - pub fn is_other(&self) -> Option { + pub fn as_other(&self) -> Option { match &self.kind { ConnectorErrorKind::Other(ek) => *ek, _ => None, diff --git a/rust-runtime/aws-smithy-http/src/retry.rs b/rust-runtime/aws-smithy-http/src/retry.rs index 30c8654926..eaf7d0e093 100644 --- a/rust-runtime/aws-smithy-http/src/retry.rs +++ b/rust-runtime/aws-smithy-http/src/retry.rs @@ -45,7 +45,7 @@ impl DefaultResponseRetryClassifier { Err(SdkError::DispatchFailure(err)) => { if err.is_timeout() || err.is_io() { Err(RetryKind::Error(ErrorKind::TransientError)) - } else if let Some(ek) = err.is_other() { + } else if let Some(ek) = err.as_other() { Err(RetryKind::Error(ek)) } else { Err(RetryKind::UnretryableFailure) diff --git a/rust-runtime/aws-smithy-protocol-test/Cargo.toml b/rust-runtime/aws-smithy-protocol-test/Cargo.toml index e18aea12b4..8d67abb2a5 100644 --- a/rust-runtime/aws-smithy-protocol-test/Cargo.toml +++ b/rust-runtime/aws-smithy-protocol-test/Cargo.toml @@ -8,15 +8,14 @@ license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" [dependencies] -http = "0.2.1" -thiserror = "1" -serde_json = "1" -regex = "1.5" # Not perfect for our needs, but good for now assert-json-diff = "1.1" - +http = "0.2.1" pretty_assertions = "1.3" +regex = "1.5" roxmltree = "0.14.1" +serde_json = "1" +thiserror = "1.0.40" [package.metadata.docs.rs] diff --git a/rust-runtime/aws-smithy-protocol-test/src/lib.rs b/rust-runtime/aws-smithy-protocol-test/src/lib.rs index da9c213d5d..d110272cea 100644 --- a/rust-runtime/aws-smithy-protocol-test/src/lib.rs +++ b/rust-runtime/aws-smithy-protocol-test/src/lib.rs @@ -276,6 +276,7 @@ pub fn require_headers( Ok(()) } +#[derive(Clone)] pub enum MediaType { /// Json media types are deserialized and compared Json, diff --git a/rust-runtime/aws-smithy-protocol-test/src/xml.rs b/rust-runtime/aws-smithy-protocol-test/src/xml.rs index c92882c5ee..a27420e36b 100644 --- a/rust-runtime/aws-smithy-protocol-test/src/xml.rs +++ b/rust-runtime/aws-smithy-protocol-test/src/xml.rs @@ -12,6 +12,9 @@ use std::fmt::Write; /// This will normalize documents and attempts to determine if it is OK to sort members or not by /// using a heuristic to determine if the tag represents a list (which should not be reordered) pub(crate) fn try_xml_equivalent(actual: &str, expected: &str) -> Result<(), ProtocolTestFailure> { + if actual == expected { + return Ok(()); + } let norm_1 = normalize_xml(actual).map_err(|e| ProtocolTestFailure::InvalidBodyFormat { expected: "actual document to be valid XML".to_string(), found: format!("{}\n{}", e, actual), diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index 9c6d96f52f..426c449ccc 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -6,15 +6,24 @@ description = "Smithy runtime types." edition = "2021" license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" -publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +client = [] +http-auth = ["dep:zeroize"] +test-util = ["aws-smithy-types/test-util"] + [dependencies] -aws-smithy-types = { path = "../aws-smithy-types" } +aws-smithy-async = { path = "../aws-smithy-async" } aws-smithy-http = { path = "../aws-smithy-http" } -tokio = { version = "1.25", features = ["sync"] } +aws-smithy-types = { path = "../aws-smithy-types" } +bytes = "1" http = "0.2.3" +tokio = { version = "1.25", features = ["sync"] } +tracing = "0.1" +zeroize = { version = "1", optional = true } [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-runtime-api/README.md b/rust-runtime/aws-smithy-runtime-api/README.md index 0e33661021..de1aa0dd0a 100644 --- a/rust-runtime/aws-smithy-runtime-api/README.md +++ b/rust-runtime/aws-smithy-runtime-api/README.md @@ -1,8 +1,14 @@ -# aws-smithy-retries +# aws-smithy-runtime-api -**This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.** +APIs needed to configure and customize the Smithy generated code. -Smithy runtime types. +Most users will not need to use this crate directly as the most frequently used +APIs are re-exported in the generated clients. However, this crate will be useful +for anyone writing a library for others to use with their generated clients. + +If you're needing to depend on this and you're not writing a library for Smithy +generated clients, then please file an issue on [smithy-rs](https://github.com/awslabs/smithy-rs) +as we likely missed re-exporting one of the APIs. This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. diff --git a/rust-runtime/aws-smithy-runtime-api/additional-ci b/rust-runtime/aws-smithy-runtime-api/additional-ci new file mode 100755 index 0000000000..b44c6c05be --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/additional-ci @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# This script contains additional CI checks to run for this specific package + +set -e + +echo "### Testing every combination of features (excluding --all-features)" +cargo hack test --feature-powerset --exclude-all-features diff --git a/rust-runtime/aws-smithy-runtime-api/external-types.toml b/rust-runtime/aws-smithy-runtime-api/external-types.toml index f752bef762..91e6e35f0c 100644 --- a/rust-runtime/aws-smithy-runtime-api/external-types.toml +++ b/rust-runtime/aws-smithy-runtime-api/external-types.toml @@ -1,7 +1,10 @@ allowed_external_types = [ + "aws_smithy_async::*", "aws_smithy_types::*", "aws_smithy_http::*", + "bytes::bytes::Bytes", + "http::request::Request", "http::response::Response", "http::uri::Uri", diff --git a/rust-runtime/aws-smithy-runtime-api/src/box_error.rs b/rust-runtime/aws-smithy-runtime-api/src/box_error.rs new file mode 100644 index 0000000000..1ed436d404 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/box_error.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// A boxed error that is `Send` and `Sync`. +pub type BoxError = Box; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index c6724ff251..d67921332f 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -3,26 +3,24 @@ * SPDX-License-Identifier: Apache-2.0 */ +pub mod endpoint; + /// Smithy identity used by auth and signing. pub mod identity; -/// Smithy interceptors for smithy clients. -/// -/// Interceptors are lifecycle hooks that can read/modify requests and responses. pub mod interceptors; pub mod orchestrator; -/// Smithy code related to retry handling and token bucket. -/// -/// This code defines when and how failed requests should be retried. It also defines the behavior -/// used to limit the rate that requests are sent. pub mod retries; -/// Runtime plugin type definitions. -pub mod runtime_plugin; -/// Smithy endpoint resolution runtime plugins -pub mod endpoints; +pub mod runtime_components; + +pub mod runtime_plugin; -/// Smithy auth runtime plugins pub mod auth; + +/// Smithy connectors and related code. +pub mod connectors; + +pub mod ser_de; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 8454d2f91c..79a28bef35 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -3,5 +3,224 @@ * SPDX-License-Identifier: Apache-2.0 */ -pub mod identity_resolver; -pub mod option_resolver; +//! APIs for request authentication. + +use crate::box_error::BoxError; +use crate::client::identity::{Identity, SharedIdentityResolver}; +use crate::client::orchestrator::HttpRequest; +use crate::client::runtime_components::{GetIdentityResolver, RuntimeComponents}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::type_erasure::TypeErasedBox; +use aws_smithy_types::Document; +use std::borrow::Cow; +use std::fmt; +use std::sync::Arc; + +/// Auth schemes for the HTTP `Authorization` header. +#[cfg(feature = "http-auth")] +pub mod http; + +/// Static auth scheme option resolver. +pub mod static_resolver; + +/// New type around an auth scheme ID. +/// +/// Each auth scheme must have a unique string identifier associated with it, +/// which is used to refer to auth schemes by the auth scheme option resolver, and +/// also used to select an identity resolver to use. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct AuthSchemeId { + scheme_id: &'static str, +} + +impl AuthSchemeId { + /// Creates a new auth scheme ID. + pub const fn new(scheme_id: &'static str) -> Self { + Self { scheme_id } + } + + /// Returns the string equivalent of this auth scheme ID. + pub const fn as_str(&self) -> &'static str { + self.scheme_id + } +} + +impl From<&'static str> for AuthSchemeId { + fn from(scheme_id: &'static str) -> Self { + Self::new(scheme_id) + } +} + +/// Parameters needed to resolve auth scheme options. +/// +/// Most generated clients will use the [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver), +/// which doesn't require any parameters for resolution (and has its own empty params struct). +/// +/// However, more complex auth scheme resolvers may need modeled parameters in order to resolve +/// the auth scheme options. For those, this params struct holds a type erased box so that any +/// kind of parameters can be contained within, and type casted by the auth scheme option resolver +/// implementation. +#[derive(Debug)] +pub struct AuthSchemeOptionResolverParams(TypeErasedBox); + +impl AuthSchemeOptionResolverParams { + /// Creates a new [`AuthSchemeOptionResolverParams`]. + pub fn new(params: T) -> Self { + Self(TypeErasedBox::new(params)) + } + + /// Returns the underlying parameters as the type `T` if they are that type. + pub fn get(&self) -> Option<&T> { + self.0.downcast_ref() + } +} + +impl Storable for AuthSchemeOptionResolverParams { + type Storer = StoreReplace; +} + +/// Resolver for auth scheme options. +/// +/// The orchestrator needs to select an auth scheme to sign requests with, and potentially +/// from several different available auth schemes. Smithy models have a number of ways +/// to specify which operations can use which auth schemes under which conditions, as +/// documented in the [Smithy spec](https://smithy.io/2.0/spec/authentication-traits.html). +/// +/// The orchestrator uses the auth scheme option resolver runtime component to resolve +/// an ordered list of options that are available to choose from for a given request. +/// This resolver can be a simple static list, such as with the +/// [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver), +/// or it can be a complex code generated resolver that incorporates parameters from both +/// the model and the resolved endpoint. +pub trait AuthSchemeOptionResolver: Send + Sync + fmt::Debug { + /// Returns a list of available auth scheme options to choose from. + fn resolve_auth_scheme_options( + &self, + params: &AuthSchemeOptionResolverParams, + ) -> Result, BoxError>; +} + +/// A shared auth scheme option resolver. +#[derive(Clone, Debug)] +pub struct SharedAuthSchemeOptionResolver(Arc); + +impl SharedAuthSchemeOptionResolver { + /// Creates a new [`SharedAuthSchemeOptionResolver`]. + pub fn new(auth_scheme_option_resolver: impl AuthSchemeOptionResolver + 'static) -> Self { + Self(Arc::new(auth_scheme_option_resolver)) + } +} + +impl AuthSchemeOptionResolver for SharedAuthSchemeOptionResolver { + fn resolve_auth_scheme_options( + &self, + params: &AuthSchemeOptionResolverParams, + ) -> Result, BoxError> { + (*self.0).resolve_auth_scheme_options(params) + } +} + +/// An auth scheme. +/// +/// Auth schemes have unique identifiers (the `scheme_id`), +/// and provide an identity resolver and a signer. +pub trait AuthScheme: Send + Sync + fmt::Debug { + /// Returns the unique identifier associated with this auth scheme. + /// + /// This identifier is used to refer to this auth scheme from the + /// [`AuthSchemeOptionResolver`], and is also associated with + /// identity resolvers in the config. + fn scheme_id(&self) -> AuthSchemeId; + + /// Returns the identity resolver that can resolve an identity for this scheme, if one is available. + /// + /// The [`AuthScheme`] doesn't actually own an identity resolver. Rather, identity resolvers + /// are configured as runtime components. The auth scheme merely chooses a compatible identity + /// resolver from the runtime components via the [`GetIdentityResolver`] trait. The trait is + /// given rather than the full set of runtime components to prevent complex resolution logic + /// involving multiple components from taking place in this function, since that's not the + /// intended use of this design. + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option; + + /// Returns the signing implementation for this auth scheme. + fn signer(&self) -> &dyn Signer; +} + +/// Container for a shared auth scheme implementation. +#[derive(Clone, Debug)] +pub struct SharedAuthScheme(Arc); + +impl SharedAuthScheme { + /// Creates a new [`SharedAuthScheme`] from the given auth scheme. + pub fn new(auth_scheme: impl AuthScheme + 'static) -> Self { + Self(Arc::new(auth_scheme)) + } +} + +impl AuthScheme for SharedAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + self.0.scheme_id() + } + + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option { + self.0.identity_resolver(identity_resolvers) + } + + fn signer(&self) -> &dyn Signer { + self.0.signer() + } +} + +/// Signing implementation for an auth scheme. +pub trait Signer: Send + Sync + fmt::Debug { + /// Sign the given request with the given identity, components, and config. + /// + /// If the provided identity is incompatible with this signer, an error must be returned. + fn sign_http_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + runtime_components: &RuntimeComponents, + config_bag: &ConfigBag, + ) -> Result<(), BoxError>; +} + +/// Endpoint configuration for the selected auth scheme. +/// +/// The configuration held by this struct originates from the endpoint rule set in the service model. +/// +/// This struct gets added to the request state by the auth orchestrator. +#[non_exhaustive] +#[derive(Clone, Debug)] +pub struct AuthSchemeEndpointConfig<'a>(Option<&'a Document>); + +impl<'a> AuthSchemeEndpointConfig<'a> { + /// Creates an empty [`AuthSchemeEndpointConfig`]. + pub fn empty() -> Self { + Self(None) + } + + /// Returns the endpoint configuration as a [`Document`]. + pub fn as_document(&self) -> Option<&'a Document> { + self.0 + } +} + +impl<'a> From> for AuthSchemeEndpointConfig<'a> { + fn from(value: Option<&'a Document>) -> Self { + Self(value) + } +} + +impl<'a> From<&'a Document> for AuthSchemeEndpointConfig<'a> { + fn from(value: &'a Document) -> Self { + Self(Some(value)) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs new file mode 100644 index 0000000000..bb6ee4cc49 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs @@ -0,0 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::auth::AuthSchemeId; + +/// Auth scheme ID for HTTP API key based authentication. +pub const HTTP_API_KEY_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-api-key-auth"); + +/// Auth scheme ID for HTTP Basic Auth. +pub const HTTP_BASIC_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-basic-auth"); + +/// Auth scheme ID for HTTP Bearer Auth. +pub const HTTP_BEARER_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-bearer-auth"); + +/// Auth scheme ID for HTTP Digest Auth. +pub const HTTP_DIGEST_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-digest-auth"); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/identity_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/identity_resolver.rs deleted file mode 100644 index 01e85ce27b..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/identity_resolver.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::client::identity::Identity; -use crate::client::orchestrator::{BoxError, IdentityResolver}; -use crate::config_bag::ConfigBag; - -#[derive(Debug)] -pub struct AnonymousIdentity {} - -impl AnonymousIdentity { - pub fn new() -> Self { - Self {} - } -} - -#[derive(Debug)] -pub struct StubIdentityResolver {} - -impl IdentityResolver for StubIdentityResolver { - fn resolve_identity(&self, _cfg: &ConfigBag) -> Result { - Ok(Identity::new(AnonymousIdentity::new(), None)) - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs deleted file mode 100644 index cad736df48..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::client::orchestrator::{ - AuthOptionResolver, AuthOptionResolverParams, BoxError, HttpAuthOption, -}; - -#[derive(Debug)] -pub struct StubAuthOptionResolver {} - -impl StubAuthOptionResolver { - pub fn new() -> Self { - Self {} - } -} - -impl AuthOptionResolver for StubAuthOptionResolver { - fn resolve_auth_options( - &self, - _params: &AuthOptionResolverParams, - ) -> Result, BoxError> { - Ok(Vec::new()) - } -} - -pub struct StubAuthOptionResolverParams {} - -impl StubAuthOptionResolverParams { - pub fn new() -> Self { - Self {} - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/static_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/static_resolver.rs new file mode 100644 index 0000000000..23781e2545 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/static_resolver.rs @@ -0,0 +1,49 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::box_error::BoxError; +use crate::client::auth::{AuthSchemeId, AuthSchemeOptionResolver, AuthSchemeOptionResolverParams}; +use std::borrow::Cow; + +/// New-type around a `Vec` that implements `AuthSchemeOptionResolver`. +#[derive(Debug)] +pub struct StaticAuthSchemeOptionResolver { + auth_scheme_options: Vec, +} + +impl StaticAuthSchemeOptionResolver { + /// Creates a new instance of `StaticAuthSchemeOptionResolver`. + pub fn new(auth_scheme_options: Vec) -> Self { + Self { + auth_scheme_options, + } + } +} + +impl AuthSchemeOptionResolver for StaticAuthSchemeOptionResolver { + fn resolve_auth_scheme_options( + &self, + _params: &AuthSchemeOptionResolverParams, + ) -> Result, BoxError> { + Ok(Cow::Borrowed(&self.auth_scheme_options)) + } +} + +/// Empty params to be used with [`StaticAuthSchemeOptionResolver`]. +#[derive(Debug)] +pub struct StaticAuthSchemeOptionResolverParams; + +impl StaticAuthSchemeOptionResolverParams { + /// Creates a new `StaticAuthSchemeOptionResolverParams`. + pub fn new() -> Self { + Self + } +} + +impl From for AuthSchemeOptionResolverParams { + fn from(params: StaticAuthSchemeOptionResolverParams) -> Self { + AuthSchemeOptionResolverParams::new(params) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs new file mode 100644 index 0000000000..79629b9af1 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; +use std::fmt; +use std::sync::Arc; + +/// Trait with a `call` function that asynchronously converts a request into a response. +/// +/// Ordinarily, a connector would use an underlying HTTP library such as [hyper](https://crates.io/crates/hyper), +/// and any associated HTTPS implementation alongside it to service requests. +/// +/// However, it can also be useful to create fake connectors implementing this trait +/// for testing. +pub trait HttpConnector: Send + Sync + fmt::Debug { + /// Asynchronously converts a request into a response. + fn call(&self, request: HttpRequest) -> BoxFuture; +} + +/// A shared [`HttpConnector`] implementation. +#[derive(Clone, Debug)] +pub struct SharedHttpConnector(Arc); + +impl SharedHttpConnector { + /// Returns a new [`SharedHttpConnector`]. + pub fn new(connection: impl HttpConnector + 'static) -> Self { + Self(Arc::new(connection)) + } +} + +impl HttpConnector for SharedHttpConnector { + fn call(&self, request: HttpRequest) -> BoxFuture { + (*self.0).call(request) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs b/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs new file mode 100644 index 0000000000..a32ed602fe --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs @@ -0,0 +1,62 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! APIs needed to configure endpoint resolution for clients. + +use crate::client::orchestrator::Future; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; +use aws_smithy_types::endpoint::Endpoint; +use aws_smithy_types::type_erasure::TypeErasedBox; +use std::fmt; +use std::sync::Arc; + +/// Parameters originating from the Smithy endpoint ruleset required for endpoint resolution. +/// +/// The actual endpoint parameters are code generated from the Smithy model, and thus, +/// are not known to the runtime crates. Hence, this struct is really a new-type around +/// a [`TypeErasedBox`] that holds the actual concrete parameters in it. +#[derive(Debug)] +pub struct EndpointResolverParams(TypeErasedBox); + +impl EndpointResolverParams { + /// Creates a new [`EndpointResolverParams`] from a concrete parameters instance. + pub fn new(params: T) -> Self { + Self(TypeErasedBox::new(params)) + } + + /// Attempts to downcast the underlying concrete parameters to `T` and return it as a reference. + pub fn get(&self) -> Option<&T> { + self.0.downcast_ref() + } +} + +impl Storable for EndpointResolverParams { + type Storer = StoreReplace; +} + +/// Configurable endpoint resolver implementation. +pub trait EndpointResolver: Send + Sync + fmt::Debug { + /// Asynchronously resolves an endpoint to use from the given endpoint parameters. + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future; +} + +/// Shared endpoint resolver. +/// +/// This is a simple shared ownership wrapper type for the [`EndpointResolver`] trait. +#[derive(Clone, Debug)] +pub struct SharedEndpointResolver(Arc); + +impl SharedEndpointResolver { + /// Creates a new [`SharedEndpointResolver`]. + pub fn new(endpoint_resolver: impl EndpointResolver + 'static) -> Self { + Self(Arc::new(endpoint_resolver)) + } +} + +impl EndpointResolver for SharedEndpointResolver { + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future { + self.0.resolve_endpoint(params) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs b/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs deleted file mode 100644 index 03a9ea083c..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::client::orchestrator::{BoxError, EndpointResolver, HttpRequest}; -use http::Uri; -use std::fmt::Debug; -use std::str::FromStr; - -#[derive(Debug, Clone)] -pub struct StaticUriEndpointResolver { - uri: Uri, -} - -impl StaticUriEndpointResolver { - pub fn localhost(port: u16) -> Self { - Self { - uri: Uri::from_str(&format!("https://localhost:{port}")) - .expect("all u16 values are valid ports"), - } - } - - pub fn uri(uri: Uri) -> Self { - Self { uri } - } -} - -impl Default for StaticUriEndpointResolver { - fn default() -> Self { - StaticUriEndpointResolver::localhost(3000) - } -} - -impl EndpointResolver for StaticUriEndpointResolver { - fn resolve_and_apply_endpoint(&self, request: &mut HttpRequest) -> Result<(), BoxError> { - *request.uri_mut() = self.uri.clone(); - - Ok(()) - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index a5dbe262ed..27c4accf4e 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -3,38 +3,137 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_types::DateTime; +use crate::client::auth::AuthSchemeId; +use crate::client::orchestrator::Future; +use aws_smithy_types::config_bag::ConfigBag; use std::any::Any; +use std::fmt; use std::fmt::Debug; use std::sync::Arc; +use std::time::SystemTime; +#[cfg(feature = "http-auth")] +pub mod http; + +/// Resolver for identities. +/// +/// Every [`AuthScheme`](crate::client::auth::AuthScheme) has one or more compatible +/// identity resolvers, which are selected from runtime components by the auth scheme +/// implementation itself. +/// +/// The identity resolver must return a [`Future`] with the resolved identity, or an error +/// if resolution failed. There is no optionality for identity resolvers. The identity either +/// resolves successfully, or it fails. The orchestrator will choose exactly one auth scheme +/// to use, and thus, its chosen identity resolver is the only identity resolver that runs. +/// There is no fallback to other auth schemes in the absense of an identity. +pub trait IdentityResolver: Send + Sync + Debug { + /// Asynchronously resolves an identity for a request using the given config. + fn resolve_identity(&self, config_bag: &ConfigBag) -> Future; +} + +/// Container for a shared identity resolver. #[derive(Clone, Debug)] +pub struct SharedIdentityResolver(Arc); + +impl SharedIdentityResolver { + /// Creates a new [`SharedIdentityResolver`] from the given resolver. + pub fn new(resolver: impl IdentityResolver + 'static) -> Self { + Self(Arc::new(resolver)) + } +} + +impl IdentityResolver for SharedIdentityResolver { + fn resolve_identity(&self, config_bag: &ConfigBag) -> Future { + self.0.resolve_identity(config_bag) + } +} + +/// An identity resolver paired with an auth scheme ID that it resolves for. +#[derive(Clone, Debug)] +pub(crate) struct ConfiguredIdentityResolver { + auth_scheme: AuthSchemeId, + identity_resolver: SharedIdentityResolver, +} + +impl ConfiguredIdentityResolver { + /// Creates a new [`ConfiguredIdentityResolver`] from the given auth scheme and identity resolver. + pub(crate) fn new( + auth_scheme: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) -> Self { + Self { + auth_scheme, + identity_resolver, + } + } + + /// Returns the auth scheme ID. + pub(crate) fn scheme_id(&self) -> AuthSchemeId { + self.auth_scheme + } + + /// Returns the identity resolver. + pub(crate) fn identity_resolver(&self) -> SharedIdentityResolver { + self.identity_resolver.clone() + } +} + +/// An identity that can be used for authentication. +/// +/// The [`Identity`] is a container for any arbitrary identity data that may be used +/// by a [`Signer`](crate::client::auth::Signer) implementation. Under the hood, it +/// has an `Arc`, and it is the responsibility of the signer to downcast +/// to the appropriate data type using the `data()` function. +/// +/// The `Identity` also holds an optional expiration time, which may duplicate +/// an expiration time on the identity data. This is because an `Arc` +/// can't be downcast to any arbitrary trait, and expiring identities are +/// common enough to be built-in. +#[derive(Clone)] pub struct Identity { data: Arc, - expiration: Option, + #[allow(clippy::type_complexity)] + data_debug: Arc) -> &dyn Debug) + Send + Sync>, + expiration: Option, } impl Identity { - pub fn new(data: impl Any + Send + Sync, expiration: Option) -> Self { + /// Creates a new identity with the given data and expiration time. + pub fn new(data: T, expiration: Option) -> Self + where + T: Any + Debug + Send + Sync, + { Self { data: Arc::new(data), + data_debug: Arc::new(|d| d.downcast_ref::().expect("type-checked") as _), expiration, } } - pub fn data(&self) -> Option<&T> { + /// Returns the raw identity data. + pub fn data(&self) -> Option<&T> { self.data.downcast_ref() } - pub fn expiration(&self) -> Option<&DateTime> { + /// Returns the expiration time for this identity, if any. + pub fn expiration(&self) -> Option<&SystemTime> { self.expiration.as_ref() } } +impl Debug for Identity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Identity") + .field("data", (self.data_debug)(&self.data)) + .field("expiration", &self.expiration) + .finish() + } +} + #[cfg(test)] mod tests { use super::*; - use aws_smithy_types::date_time::Format; + use aws_smithy_async::time::{SystemTimeSource, TimeSource}; #[test] fn check_send_sync() { @@ -50,8 +149,8 @@ mod tests { last: String, } - let expiration = - DateTime::from_str("2023-03-15T00:00:00.000Z", Format::DateTimeWithOffset).unwrap(); + let ts = SystemTimeSource::new(); + let expiration = ts.now(); let identity = Identity::new( MyIdentityData { first: "foo".into(), diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs new file mode 100644 index 0000000000..878943c545 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs @@ -0,0 +1,130 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Identity types for HTTP auth + +use crate::client::identity::{Identity, IdentityResolver}; +use crate::client::orchestrator::Future; +use aws_smithy_types::config_bag::ConfigBag; +use std::fmt::Debug; +use std::sync::Arc; +use std::time::SystemTime; +use zeroize::Zeroizing; + +/// Identity type required to sign requests using Smithy's token-based HTTP auth schemes +/// +/// This `Token` type is used with Smithy's `@httpApiKeyAuth` and `@httpBearerAuth` +/// auth traits. +#[derive(Clone, Eq, PartialEq)] +pub struct Token(Arc); + +#[derive(Eq, PartialEq)] +struct TokenInner { + token: Zeroizing, + expiration: Option, +} + +impl Debug for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Token") + .field("token", &"** redacted **") + .finish() + } +} + +impl Token { + /// Constructs a new identity token for HTTP auth. + pub fn new(token: impl Into, expiration: Option) -> Self { + Self(Arc::new(TokenInner { + token: Zeroizing::new(token.into()), + expiration, + })) + } + + /// Returns the underlying identity token. + pub fn token(&self) -> &str { + &self.0.token + } +} + +impl From<&str> for Token { + fn from(token: &str) -> Self { + Self::from(token.to_owned()) + } +} + +impl From for Token { + fn from(api_key: String) -> Self { + Self(Arc::new(TokenInner { + token: Zeroizing::new(api_key), + expiration: None, + })) + } +} + +impl IdentityResolver for Token { + fn resolve_identity(&self, _config_bag: &ConfigBag) -> Future { + Future::ready(Ok(Identity::new(self.clone(), self.0.expiration))) + } +} + +/// Identity type required to sign requests using Smithy's login-based HTTP auth schemes +/// +/// This `Login` type is used with Smithy's `@httpBasicAuth` and `@httpDigestAuth` +/// auth traits. +#[derive(Clone, Eq, PartialEq)] +pub struct Login(Arc); + +#[derive(Eq, PartialEq)] +struct LoginInner { + user: String, + password: Zeroizing, + expiration: Option, +} + +impl Debug for Login { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Login") + .field("user", &self.0.user) + .field("password", &"** redacted **") + .finish() + } +} + +impl Login { + /// Constructs a new identity login for HTTP auth. + pub fn new( + user: impl Into, + password: impl Into, + expiration: Option, + ) -> Self { + Self(Arc::new(LoginInner { + user: user.into(), + password: Zeroizing::new(password.into()), + expiration, + })) + } + + /// Returns the login user. + pub fn user(&self) -> &str { + &self.0.user + } + + /// Returns the login password. + pub fn password(&self) -> &str { + &self.0.password + } + + /// Returns the expiration time of this login (if any) + pub fn expiration(&self) -> Option { + self.0.expiration + } +} + +impl IdentityResolver for Login { + fn resolve_identity(&self, _config_bag: &ConfigBag) -> Future { + Future::ready(Ok(Identity::new(self.clone(), self.0.expiration))) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index f584c3752c..443af2962a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -3,23 +3,52 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Interceptors for clients. +//! +//! Interceptors are operation lifecycle hooks that can read/modify requests and responses. + +use crate::box_error::BoxError; +use crate::client::interceptors::context::{ + AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, + BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, + BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, + FinalizerInterceptorContextRef, +}; +use crate::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use std::fmt; +use std::marker::PhantomData; +use std::ops::Deref; +use std::sync::Arc; + pub mod context; pub mod error; -use crate::config_bag::ConfigBag; -pub use context::InterceptorContext; pub use error::InterceptorError; macro_rules! interceptor_trait_fn { - ($name:ident, $docs:tt) => { + ($name:ident, $phase:ident, $docs:tt) => { + #[doc = $docs] + fn $name( + &self, + context: &$phase<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); + Ok(()) + } + }; + (mut $name:ident, $phase:ident, $docs:tt) => { #[doc = $docs] fn $name( - &mut self, - context: &InterceptorContext, + &self, + context: &mut $phase<'_>, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - let _ctx = context; - let _cfg = cfg; + ) -> Result<(), BoxError> { + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); Ok(()) } }; @@ -35,32 +64,39 @@ macro_rules! interceptor_trait_fn { /// of the SDK ’s request execution pipeline. Hooks are either "read" hooks, which make it possible /// to read in-flight request or response messages, or "read/write" hooks, which make it possible /// to modify in-flight request or output messages. -pub trait Interceptor { - interceptor_trait_fn!( - read_before_execution, - " - A hook called at the start of an execution, before the SDK - does anything else. - - **When:** This will **ALWAYS** be called once per execution. The duration - between invocation of this hook and `after_execution` is very close - to full duration of the execution. - - **Available Information:** The [InterceptorContext::input()] is - **ALWAYS** available. Other information **WILL NOT** be available. - - **Error Behavior:** Errors raised by this hook will be stored - until all interceptors have had their `before_execution` invoked. - Other hooks will then be skipped and execution will jump to - `modify_before_completion` with the raised error as the - [InterceptorContext::output_or_error()]. If multiple - `before_execution` methods raise errors, the latest - will be used and earlier ones will be logged and dropped. - " - ); +pub trait Interceptor: fmt::Debug + Send + Sync { + /// The name of this interceptor, used in error messages for debugging. + fn name(&self) -> &'static str; + + /// A hook called at the start of an execution, before the SDK + /// does anything else. + /// + /// **When:** This will **ALWAYS** be called once per execution. The duration + /// between invocation of this hook and `after_execution` is very close + /// to full duration of the execution. + /// + /// **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) + /// is **ALWAYS** available. Other information **WILL NOT** be available. + /// + /// **Error Behavior:** Errors raised by this hook will be stored + /// until all interceptors have had their `before_execution` invoked. + /// Other hooks will then be skipped and execution will jump to + /// `modify_before_completion` with the raised error as the + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). If multiple + /// `before_execution` methods raise errors, the latest + /// will be used and earlier ones will be logged and dropped. + fn read_before_execution( + &self, + context: &BeforeSerializationInterceptorContextRef<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let (_ctx, _cfg) = (context, cfg); + Ok(()) + } interceptor_trait_fn!( - modify_before_serialization, + mut modify_before_serialization, + BeforeSerializationInterceptorContextMut, " A hook called before the input message is marshalled into a transport message. @@ -70,15 +106,14 @@ pub trait Interceptor { **When:** This will **ALWAYS** be called once per execution, except when a failure occurs earlier in the request pipeline. - **Available Information:** The [InterceptorContext::input()] is + **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) is **ALWAYS** available. This request may have been modified by earlier `modify_before_serialization` hooks, and may be modified further by later hooks. Other information **WILL NOT** be available. **Error Behavior:** If errors are raised by this hook, - execution will jump to `modify_before_completion` with the raised - error as the [InterceptorContext::output_or_error()]. + error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The input message returned by this hook MUST be the same type of input message passed into this hook. @@ -88,6 +123,7 @@ pub trait Interceptor { interceptor_trait_fn!( read_before_serialization, + BeforeSerializationInterceptorContextRef, " A hook called before the input message is marshalled into a transport @@ -98,50 +134,50 @@ pub trait Interceptor { duration between invocation of this hook and `after_serialization` is very close to the amount of time spent marshalling the request. - **Available Information:** The [InterceptorContext::input()] is + **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) is **ALWAYS** available. Other information **WILL NOT** be available. **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_completion` with the raised - error as the [InterceptorContext::output_or_error()]. + error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); interceptor_trait_fn!( read_after_serialization, + BeforeTransmitInterceptorContextRef, " - /// A hook called after the input message is marshalled into - /// a transport message. - /// - /// **When:** This will **ALWAYS** be called once per execution, except when a - /// failure occurs earlier in the request pipeline. The duration - /// between invocation of this hook and `before_serialization` is very - /// close to the amount of time spent marshalling the request. - /// - /// **Available Information:** The [InterceptorContext::input()] - /// and [InterceptorContext::request()] are **ALWAYS** available. - /// Other information **WILL NOT** be available. - /// - /// **Error Behavior:** If errors are raised by this hook, - /// execution will jump to `modify_before_completion` with the raised - /// error as the [InterceptorContext::output_or_error()]. + A hook called after the input message is marshalled into + a transport message. + + **When:** This will **ALWAYS** be called once per execution, except when a + failure occurs earlier in the request pipeline. The duration + between invocation of this hook and `before_serialization` is very + close to the amount of time spent marshalling the request. + + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. Other information **WILL NOT** be available. + + **Error Behavior:** If errors are raised by this hook, + execution will jump to `modify_before_completion` with the raised + error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); interceptor_trait_fn!( - modify_before_retry_loop, + mut modify_before_retry_loop, + BeforeTransmitInterceptorContextMut, " A hook called before the retry loop is entered. This method has the ability to modify and return a new transport request message of the same type, except when a failure occurs earlier in the request pipeline. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. - Other information **WILL NOT** be available. + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. Other information **WILL NOT** be available. **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_completion` with the raised - error as the [InterceptorContext::output_or_error()]. + error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The transport request message returned by this hook MUST be the same type of request message passed into this hook @@ -151,6 +187,7 @@ pub trait Interceptor { interceptor_trait_fn!( read_before_attempt, + BeforeTransmitInterceptorContextRef, " A hook called before each attempt at sending the transmission request message to the service. @@ -159,9 +196,8 @@ pub trait Interceptor { failure occurs earlier in the request pipeline. This method will be called multiple times in the event of retries. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. - Other information **WILL NOT** be available. In the event of retries, + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). @@ -169,14 +205,15 @@ pub trait Interceptor { until all interceptors have had their `before_attempt` invoked. Other hooks will then be skipped and execution will jump to `modify_before_attempt_completion` with the raised error as the - [InterceptorContext::output_or_error()]. If multiple + [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). If multiple `before_attempt` methods raise errors, the latest will be used and earlier ones will be logged and dropped. " ); interceptor_trait_fn!( - modify_before_signing, + mut modify_before_signing, + BeforeTransmitInterceptorContextMut, " A hook called before the transport request message is signed. This method has the ability to modify and return a new transport @@ -186,18 +223,16 @@ pub trait Interceptor { failure occurs earlier in the request pipeline. This method may be called multiple times in the event of retries. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. - The `http::Request` may have been modified by earlier + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. The `http::Request` may have been modified by earlier `modify_before_signing` hooks, and may be modified further by later hooks. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made - in previous attempts - (e.g. by request signers or other interceptors). + in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The transport request message returned by this hook MUST be the same type of request message passed into this hook @@ -208,6 +243,7 @@ pub trait Interceptor { interceptor_trait_fn!( read_before_signing, + BeforeTransmitInterceptorContextRef, " A hook called before the transport request message is signed. @@ -217,20 +253,20 @@ pub trait Interceptor { invocation of this hook and `after_signing` is very close to the amount of time spent signing the request. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); interceptor_trait_fn!( read_after_signing, + BeforeTransmitInterceptorContextRef, " A hook called after the transport request message is signed. @@ -240,41 +276,40 @@ pub trait Interceptor { invocation of this hook and `before_signing` is very close to the amount of time spent signing the request. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); interceptor_trait_fn!( - modify_before_transmit, + mut modify_before_transmit, + BeforeTransmitInterceptorContextMut, " - /// A hook called before the transport request message is sent to the - /// service. This method has the ability to modify and return - /// a new transport request message of the same type. - /// - /// **When:** This will **ALWAYS** be called once per attempt, except when a - /// failure occurs earlier in the request pipeline. This method may be - /// called multiple times in the event of retries. - /// - /// **Available Information:** The [InterceptorContext::input()] - /// and [InterceptorContext::request()] are **ALWAYS** available. - /// The `http::Request` may have been modified by earlier - /// `modify_before_transmit` hooks, and may be modified further by later - /// hooks. Other information **WILL NOT** be available. - /// In the event of retries, the `InterceptorContext` will not include - /// changes made in previous attempts (e.g. by request signers or + A hook called before the transport request message is sent to the + service. This method has the ability to modify and return + a new transport request message of the same type. + + **When:** This will **ALWAYS** be called once per attempt, except when a + failure occurs earlier in the request pipeline. This method may be + called multiple times in the event of retries. + + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. The `http::Request` may have been modified by earlier + `modify_before_transmit` hooks, and may be modified further by later + hooks. Other information **WILL NOT** be available. + In the event of retries, the `InterceptorContext` will not include + changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The transport request message returned by this hook MUST be the same type of request message passed into this hook @@ -285,6 +320,7 @@ pub trait Interceptor { interceptor_trait_fn!( read_before_transmit, + BeforeTransmitInterceptorContextRef, " A hook called before the transport request message is sent to the service. @@ -297,21 +333,21 @@ pub trait Interceptor { Depending on the protocol, the duration may not include the time spent reading the response data. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. - Other information **WILL NOT** be available. In the event of retries, + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); interceptor_trait_fn!( read_after_transmit, + BeforeDeserializationInterceptorContextRef, " A hook called after the transport request message is sent to the service and a transport response message is received. @@ -324,21 +360,20 @@ pub trait Interceptor { Depending on the protocol, the duration may not include the time spent reading the response data. - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()] and - [InterceptorContext::response()] are **ALWAYS** available. - Other information **WILL NOT** be available. In the event of retries, + **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response) + is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); interceptor_trait_fn!( - modify_before_deserialization, + mut modify_before_deserialization, + BeforeDeserializationInterceptorContextMut, " A hook called before the transport response message is unmarshalled. This method has the ability to modify and return a new transport @@ -348,10 +383,8 @@ pub trait Interceptor { failure occurs earlier in the request pipeline. This method may be called multiple times in the event of retries. - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()] and - [InterceptorContext::response()] are **ALWAYS** available. - The transmit_response may have been modified by earlier + **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response) + is **ALWAYS** available. The transmit_response may have been modified by earlier `modify_before_deserialization` hooks, and may be modified further by later hooks. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in @@ -360,7 +393,7 @@ pub trait Interceptor { **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with the raised error as the - [InterceptorContext::output_or_error()]. + [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The transport response message returned by this hook MUST be the same type of response message passed into @@ -370,6 +403,7 @@ pub trait Interceptor { interceptor_trait_fn!( read_before_deserialization, + BeforeDeserializationInterceptorContextRef, " A hook called before the transport response message is unmarshalled @@ -381,21 +415,20 @@ pub trait Interceptor { Depending on the protocol and operation, the duration may include the time spent downloading the response data. - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()] and - [InterceptorContext::response()] are **ALWAYS** available. - Other information **WILL NOT** be available. In the event of retries, + **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response) + is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` - with the raised error as the [InterceptorContext::output_or_error()]. + with the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); interceptor_trait_fn!( read_after_deserialization, + AfterDeserializationInterceptorContextRef, " A hook called after the transport response message is unmarshalled. @@ -407,216 +440,219 @@ pub trait Interceptor { the duration may include the time spent downloading the response data. - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()], - [InterceptorContext::response()] and - [InterceptorContext::output_or_error()] are **ALWAYS** available. In the event - of retries, the `InterceptorContext` will not include changes made + **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response) + and [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) + are **ALWAYS** available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); - interceptor_trait_fn!( - modify_before_attempt_completion, - " - A hook called when an attempt is completed. This method has the - ability to modify and return a new output message or error - matching the currently-executing operation. - - **When:** This will **ALWAYS** be called once per attempt, except when a - failure occurs before `before_attempt`. This method may - be called multiple times in the event of retries. - - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()], - [InterceptorContext::response()] and - [InterceptorContext::output_or_error()] are **ALWAYS** available. In the event - of retries, the `InterceptorContext` will not include changes made - in previous attempts (e.g. by request signers or other interceptors). - - **Error Behavior:** If errors are raised by this - hook, execution will jump to `after_attempt` with - the raised error as the [InterceptorContext::output_or_error()]. - - **Return Constraints:** Any output message returned by this - hook MUST match the operation being invoked. Any error type can be - returned, replacing the response currently in the context. - " - ); - - interceptor_trait_fn!( - read_after_attempt, - " - A hook called when an attempt is completed. - - **When:** This will **ALWAYS** be called once per attempt, as long as - `before_attempt` has been executed. - - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()] and - [InterceptorContext::output_or_error()] are **ALWAYS** available. - The [InterceptorContext::response()] is available if a - response was received by the service for this attempt. - In the event of retries, the `InterceptorContext` will not include - changes made in previous attempts (e.g. by request signers or other - interceptors). - - **Error Behavior:** Errors raised by this hook will be stored - until all interceptors have had their `after_attempt` invoked. - If multiple `after_execution` methods raise errors, the latest - will be used and earlier ones will be logged and dropped. If the - retry strategy determines that the execution is retryable, - execution will then jump to `before_attempt`. Otherwise, - execution will jump to `modify_before_attempt_completion` with the - raised error as the [InterceptorContext::output_or_error()]. - " - ); - - interceptor_trait_fn!( - modify_before_completion, - " - A hook called when an execution is completed. - This method has the ability to modify and return a new - output message or error matching the currently - executing - operation. - - **When:** This will **ALWAYS** be called once per execution. - - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::output_or_error()] are **ALWAYS** available. The - [InterceptorContext::request()] - and [InterceptorContext::response()] are available if the - execution proceeded far enough for them to be generated. - - **Error Behavior:** If errors are raised by this - hook , execution will jump to `after_attempt` with - the raised error as the [InterceptorContext::output_or_error()]. - - **Return Constraints:** Any output message returned by this - hook MUST match the operation being invoked. Any error type can be - returned , replacing the response currently in the context. - " - ); + /// A hook called when an attempt is completed. This method has the + /// ability to modify and return a new output message or error + /// matching the currently-executing operation. + /// + /// **When:** This will **ALWAYS** be called once per attempt, except when a + /// failure occurs before `before_attempt`. This method may + /// be called multiple times in the event of retries. + /// + /// **Available Information:** + /// The [`InterceptorContext::input`](context::InterceptorContext::input), + /// [`InterceptorContext::request`](context::InterceptorContext::request), + /// [`InterceptorContext::response`](context::InterceptorContext::response), or + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available. + /// If the operation succeeded, the `output` will be available. Otherwise, any of the other + /// pieces of information may be available depending on where in the operation lifecycle it failed. + /// In the event of retries, the `InterceptorContext` will not include changes made + /// in previous attempts (e.g. by request signers or other interceptors). + /// + /// **Error Behavior:** If errors are raised by this + /// hook, execution will jump to `after_attempt` with + /// the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). + /// + /// **Return Constraints:** Any output message returned by this + /// hook MUST match the operation being invoked. Any error type can be + /// returned, replacing the response currently in the context. + fn modify_before_attempt_completion( + &self, + context: &mut FinalizerInterceptorContextMut<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); + Ok(()) + } - interceptor_trait_fn!( - read_after_execution, - " - A hook called when an execution is completed. + /// A hook called when an attempt is completed. + /// + /// **When:** This will **ALWAYS** be called once per attempt, as long as + /// `before_attempt` has been executed. + /// + /// **Available Information:** + /// The [`InterceptorContext::input`](context::InterceptorContext::input), + /// [`InterceptorContext::request`](context::InterceptorContext::request), + /// [`InterceptorContext::response`](context::InterceptorContext::response), or + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available. + /// If the operation succeeded, the `output` will be available. Otherwise, any of the other + /// pieces of information may be available depending on where in the operation lifecycle it failed. + /// In the event of retries, the `InterceptorContext` will not include changes made + /// in previous attempts (e.g. by request signers or other interceptors). + /// + /// **Error Behavior:** Errors raised by this hook will be stored + /// until all interceptors have had their `after_attempt` invoked. + /// If multiple `after_execution` methods raise errors, the latest + /// will be used and earlier ones will be logged and dropped. If the + /// retry strategy determines that the execution is retryable, + /// execution will then jump to `before_attempt`. Otherwise, + /// execution will jump to `modify_before_attempt_completion` with the + /// raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). + fn read_after_attempt( + &self, + context: &FinalizerInterceptorContextRef<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); + Ok(()) + } - **When:** This will **ALWAYS** be called once per execution. The duration - between invocation of this hook and `before_execution` is very - close to the full duration of the execution. + /// A hook called when an execution is completed. + /// This method has the ability to modify and return a new + /// output message or error matching the currently - executing + /// operation. + /// + /// **When:** This will **ALWAYS** be called once per execution. + /// + /// **Available Information:** + /// The [`InterceptorContext::input`](context::InterceptorContext::input), + /// [`InterceptorContext::request`](context::InterceptorContext::request), + /// [`InterceptorContext::response`](context::InterceptorContext::response), or + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available. + /// If the operation succeeded, the `output` will be available. Otherwise, any of the other + /// pieces of information may be available depending on where in the operation lifecycle it failed. + /// In the event of retries, the `InterceptorContext` will not include changes made + /// in previous attempts (e.g. by request signers or other interceptors). + /// + /// **Error Behavior:** If errors are raised by this + /// hook , execution will jump to `after_attempt` with + /// the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). + /// + /// **Return Constraints:** Any output message returned by this + /// hook MUST match the operation being invoked. Any error type can be + /// returned , replacing the response currently in the context. + fn modify_before_completion( + &self, + context: &mut FinalizerInterceptorContextMut<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); + Ok(()) + } - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::output_or_error()] are **ALWAYS** available. The - [InterceptorContext::request()] and - [InterceptorContext::response()] are available if the - execution proceeded far enough for them to be generated. + /// A hook called when an execution is completed. + /// + /// **When:** This will **ALWAYS** be called once per execution. The duration + /// between invocation of this hook and `before_execution` is very + /// close to the full duration of the execution. + /// + /// **Available Information:** + /// The [`InterceptorContext::input`](context::InterceptorContext::input), + /// [`InterceptorContext::request`](context::InterceptorContext::request), + /// [`InterceptorContext::response`](context::InterceptorContext::response), or + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available. + /// If the operation succeeded, the `output` will be available. Otherwise, any of the other + /// pieces of information may be available depending on where in the operation lifecycle it failed. + /// In the event of retries, the `InterceptorContext` will not include changes made + /// in previous attempts (e.g. by request signers or other interceptors). + /// + /// **Error Behavior:** Errors raised by this hook will be stored + /// until all interceptors have had their `after_execution` invoked. + /// The error will then be treated as the + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) + /// to the customer. If multiple `after_execution` methods raise errors , the latest will be + /// used and earlier ones will be logged and dropped. + fn read_after_execution( + &self, + context: &FinalizerInterceptorContextRef<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); + Ok(()) + } +} - **Error Behavior:** Errors raised by this hook will be stored - until all interceptors have had their `after_execution` invoked. - The error will then be treated as the - [InterceptorContext::output_or_error()] to the customer. If multiple - `after_execution` methods raise errors , the latest will be - used and earlier ones will be logged and dropped. - " - ); +/// Interceptor wrapper that may be shared +#[derive(Clone)] +pub struct SharedInterceptor { + interceptor: Arc, + check_enabled: Arc bool + Send + Sync>, } -pub struct Interceptors { - client_interceptors: Vec>>, - operation_interceptors: Vec>>, +impl fmt::Debug for SharedInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SharedInterceptor") + .field("interceptor", &self.interceptor) + .finish() + } } -impl Default for Interceptors { - fn default() -> Self { +impl SharedInterceptor { + /// Create a new `SharedInterceptor` from `Interceptor`. + pub fn new(interceptor: T) -> Self { Self { - client_interceptors: Vec::new(), - operation_interceptors: Vec::new(), + interceptor: Arc::new(interceptor), + check_enabled: Arc::new(|conf: &ConfigBag| { + conf.load::>().is_none() + }), } } -} -macro_rules! interceptor_impl_fn { - (context, $name:ident) => { - interceptor_impl_fn!(context, $name, $name); - }; - (mut context, $name:ident) => { - interceptor_impl_fn!(mut context, $name, $name); - }; - (context, $outer_name:ident, $inner_name:ident) => { - pub fn $outer_name( - &mut self, - context: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - for interceptor in self.client_interceptors.iter_mut() { - interceptor.$inner_name(context, cfg)?; - } - Ok(()) - } - }; - (mut context, $outer_name:ident, $inner_name:ident) => { - pub fn $outer_name( - &mut self, - context: &mut InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - for interceptor in self.client_interceptors.iter_mut() { - interceptor.$inner_name(context, cfg)?; - } - Ok(()) - } - }; + /// Checks if this interceptor is enabled in the given config. + pub fn enabled(&self, conf: &ConfigBag) -> bool { + (self.check_enabled)(conf) + } } -impl Interceptors { - pub fn new() -> Self { - Self::default() +impl AsRef for SharedInterceptor { + fn as_ref(&self) -> &(dyn Interceptor + 'static) { + self.interceptor.as_ref() } +} - pub fn with_client_interceptor( - &mut self, - interceptor: impl Interceptor + 'static, - ) -> &mut Self { - self.client_interceptors.push(Box::new(interceptor)); - self +impl Deref for SharedInterceptor { + type Target = Arc; + fn deref(&self) -> &Self::Target { + &self.interceptor } +} - pub fn with_operation_interceptor( - &mut self, - interceptor: impl Interceptor + 'static, - ) -> &mut Self { - self.operation_interceptors.push(Box::new(interceptor)); - self - } +/// Generalized interceptor disabling interface +/// +/// RuntimePlugins can disable interceptors by inserting [`DisableInterceptor`](DisableInterceptor) into the config bag +#[must_use] +#[derive(Debug)] +pub struct DisableInterceptor { + _t: PhantomData, + #[allow(unused)] + cause: &'static str, +} - interceptor_impl_fn!(context, client_read_before_execution, read_before_execution); - interceptor_impl_fn!( - context, - operation_read_before_execution, - read_before_execution - ); - interceptor_impl_fn!(mut context, modify_before_serialization); - interceptor_impl_fn!(context, read_before_serialization); - interceptor_impl_fn!(context, read_after_serialization); - interceptor_impl_fn!(mut context, modify_before_retry_loop); - interceptor_impl_fn!(context, read_before_attempt); - interceptor_impl_fn!(mut context, modify_before_signing); - interceptor_impl_fn!(context, read_before_signing); - interceptor_impl_fn!(context, read_after_signing); - interceptor_impl_fn!(mut context, modify_before_transmit); - interceptor_impl_fn!(context, read_before_transmit); - interceptor_impl_fn!(context, read_after_transmit); - interceptor_impl_fn!(mut context, modify_before_deserialization); - interceptor_impl_fn!(context, read_before_deserialization); - interceptor_impl_fn!(context, read_after_deserialization); - interceptor_impl_fn!(mut context, modify_before_attempt_completion); - interceptor_impl_fn!(context, read_after_attempt); - interceptor_impl_fn!(mut context, modify_before_completion); - interceptor_impl_fn!(context, read_after_execution); +impl Storable for DisableInterceptor +where + T: fmt::Debug + Send + Sync + 'static, +{ + type Storer = StoreReplace; +} + +/// Disable an interceptor with a given cause +pub fn disable_interceptor(cause: &'static str) -> DisableInterceptor { + DisableInterceptor { + _t: PhantomData::default(), + cause, + } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 4a3cb7bfe7..63a54767d4 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -3,139 +3,364 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::InterceptorError; -use crate::type_erasure::TypeErasedBox; +//! Interceptor context. +//! +//! Interceptors have access to varying pieces of context during the course of an operation. +//! +//! An operation is composed of multiple phases. The initial phase is "before serialization", which +//! has the original input as context. The next phase is "before transmit", which has the serialized +//! request as context. Depending on which hook is being called with the dispatch context, +//! the serialized request may or may not be signed (which should be apparent from the hook name). +//! Following the "before transmit" phase is the "before deserialization" phase, which has +//! the raw response available as context. Finally, the "after deserialization" phase +//! has both the raw and parsed response available. +//! +//! To summarize: +//! 1. Before serialization: Only has the operation input. +//! 2. Before transmit: Only has the serialized request. +//! 3. Before deserialization: Has the raw response. +//! 3. After deserialization: Has the raw response and the parsed response. +//! +//! When implementing hooks, if information from a previous phase is required, then implement +//! an earlier hook to examine that context, and save off any necessary information into the +//! [`ConfigBag`] for later hooks to examine. Interior mutability is **NOT** +//! recommended for storing request-specific information in your interceptor implementation. +//! Use the [`ConfigBag`] instead. -pub type Input = TypeErasedBox; -pub type Output = TypeErasedBox; -pub type Error = TypeErasedBox; -pub type OutputOrError = Result; +use crate::client::orchestrator::{HttpRequest, HttpResponse, OrchestratorError}; +use aws_smithy_http::result::SdkError; +use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; +use phase::Phase; +use std::fmt::Debug; +use std::{fmt, mem}; +use tracing::{debug, error, trace}; + +macro_rules! new_type_box { + ($name:ident, $doc:literal) => { + new_type_box!($name, TypeErasedBox, $doc, Send, Sync, fmt::Debug,); + }; + ($name:ident, $underlying:ident, $doc:literal, $($additional_bound:path,)*) => { + #[doc = $doc] + #[derive(Debug)] + pub struct $name($underlying); + + impl $name { + #[doc = concat!("Creates a new `", stringify!($name), "` with the provided concrete input value.")] + pub fn erase(input: T) -> Self { + Self($underlying::new(input)) + } + + #[doc = concat!("Downcasts to the concrete input value.")] + pub fn downcast_ref(&self) -> Option<&T> { + self.0.downcast_ref() + } + + #[doc = concat!("Downcasts to the concrete input value.")] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.0.downcast_mut() + } + + #[doc = concat!("Downcasts to the concrete input value.")] + pub fn downcast(self) -> Result { + self.0.downcast::().map(|v| *v).map_err(Self) + } + + #[doc = concat!("Returns a `", stringify!($name), "` with a fake/test value with the expectation that it won't be downcast in the test.")] + #[cfg(feature = "test-util")] + pub fn doesnt_matter() -> Self { + Self($underlying::doesnt_matter()) + } + } + }; +} + +new_type_box!(Input, "Type-erased operation input."); +new_type_box!(Output, "Type-erased operation output."); +new_type_box!( + Error, + TypeErasedError, + "Type-erased operation error.", + std::error::Error, +); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } +} + +/// Type-erased result for an operation. +pub type OutputOrError = Result>; + +type Request = HttpRequest; +type Response = HttpResponse; + +pub use wrappers::{ + AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, + BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, + BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, + FinalizerInterceptorContextRef, +}; + +mod wrappers; + +/// Operation phases. +pub(crate) mod phase; /// A container for the data currently available to an interceptor. -pub struct InterceptorContext { - input: Option, - output_or_error: Option, - request: Option, - response: Option, +/// +/// Different context is available based on which phase the operation is currently in. For example, +/// context in the "before serialization" phase won't have a `request` yet since the input hasn't been +/// serialized at that point. But once it gets into the "before transmit" phase, the `request` will be set. +#[derive(Debug)] +pub struct InterceptorContext { + pub(crate) input: Option, + pub(crate) output_or_error: Option>>, + pub(crate) request: Option, + pub(crate) response: Option, + phase: Phase, + tainted: bool, + request_checkpoint: Option, } -// TODO(interceptors) we could use types to ensure that people calling methods on interceptor context can't access -// field that haven't been set yet. -impl InterceptorContext { - pub fn new(input: Input) -> Self { - Self { +impl InterceptorContext { + /// Creates a new interceptor context in the "before serialization" phase. + pub fn new(input: Input) -> InterceptorContext { + InterceptorContext { input: Some(input), output_or_error: None, request: None, response: None, + phase: Phase::BeforeSerialization, + tainted: false, + request_checkpoint: None, } } +} +impl InterceptorContext { /// Retrieve the input for the operation being invoked. - pub fn input(&self) -> Result<&Input, InterceptorError> { - self.input - .as_ref() - .ok_or_else(InterceptorError::invalid_input_access) + pub fn input(&self) -> Option<&I> { + self.input.as_ref() } /// Retrieve the input for the operation being invoked. - pub fn input_mut(&mut self) -> Result<&mut Input, InterceptorError> { - self.input - .as_mut() - .ok_or_else(InterceptorError::invalid_input_access) + pub fn input_mut(&mut self) -> Option<&mut I> { + self.input.as_mut() } /// Takes ownership of the input. - pub fn take_input(&mut self) -> Option { + pub fn take_input(&mut self) -> Option { self.input.take() } + /// Set the request for the operation being invoked. + pub fn set_request(&mut self, request: Request) { + self.request = Some(request); + } + /// Retrieve the transmittable request for the operation being invoked. /// This will only be available once request marshalling has completed. - pub fn request(&self) -> Result<&Request, InterceptorError> { - self.request - .as_ref() - .ok_or_else(InterceptorError::invalid_request_access) + pub fn request(&self) -> Option<&Request> { + self.request.as_ref() } /// Retrieve the transmittable request for the operation being invoked. /// This will only be available once request marshalling has completed. - pub fn request_mut(&mut self) -> Result<&mut Request, InterceptorError> { - self.request - .as_mut() - .ok_or_else(InterceptorError::invalid_request_access) + pub fn request_mut(&mut self) -> Option<&mut Request> { + self.request.as_mut() } - /// Retrieve the response to the transmittable response for the operation - /// being invoked. This will only be available once transmission has - /// completed. - pub fn response(&self) -> Result<&Response, InterceptorError> { - self.response - .as_ref() - .ok_or_else(InterceptorError::invalid_response_access) + /// Takes ownership of the request. + pub fn take_request(&mut self) -> Option { + self.request.take() } - /// Retrieve the response to the transmittable response for the operation - /// being invoked. This will only be available once transmission has - /// completed. - pub fn response_mut(&mut self) -> Result<&mut Response, InterceptorError> { - self.response - .as_mut() - .ok_or_else(InterceptorError::invalid_response_access) + /// Set the response for the operation being invoked. + pub fn set_response(&mut self, response: Response) { + self.response = Some(response); + } + + /// Returns the response. + pub fn response(&self) -> Option<&Response> { + self.response.as_ref() + } + + /// Returns a mutable reference to the response. + pub fn response_mut(&mut self) -> Option<&mut Response> { + self.response.as_mut() + } + + /// Set the output or error for the operation being invoked. + pub fn set_output_or_error(&mut self, output: Result>) { + self.output_or_error = Some(output); + } + + /// Returns the deserialized output or error. + pub fn output_or_error(&self) -> Option>> { + self.output_or_error.as_ref().map(Result::as_ref) } - /// Retrieve the response to the customer. This will only be available - /// once the `response` has been unmarshalled or the attempt/execution has failed. - pub fn output_or_error(&self) -> Result, InterceptorError> { + /// Returns the mutable reference to the deserialized output or error. + pub fn output_or_error_mut(&mut self) -> Option<&mut Result>> { + self.output_or_error.as_mut() + } + + /// Return `true` if this context's `output_or_error` is an error. Otherwise, return `false`. + pub fn is_failed(&self) -> bool { self.output_or_error .as_ref() - .ok_or_else(InterceptorError::invalid_output_access) - .map(|res| res.as_ref()) + .map(Result::is_err) + .unwrap_or_default() } - /// Retrieve the response to the customer. This will only be available - /// once the `response` has been unmarshalled or the - /// attempt/execution has failed. - pub fn output_or_error_mut(&mut self) -> Result<&mut Result, InterceptorError> { - self.output_or_error - .as_mut() - .ok_or_else(InterceptorError::invalid_output_access) + /// Advance to the Serialization phase. + #[doc(hidden)] + pub fn enter_serialization_phase(&mut self) { + debug!("entering \'serialization\' phase"); + debug_assert!( + self.phase.is_before_serialization(), + "called enter_serialization_phase but phase is not before 'serialization'" + ); + self.phase = Phase::Serialization; } - // There is no set_input method because that can only be set once, during context construction + /// Advance to the BeforeTransmit phase. + #[doc(hidden)] + pub fn enter_before_transmit_phase(&mut self) { + debug!("entering \'before transmit\' phase"); + debug_assert!( + self.phase.is_serialization(), + "called enter_before_transmit_phase but phase is not 'serialization'" + ); + debug_assert!( + self.input.is_none(), + "input must be taken before calling enter_before_transmit_phase" + ); + debug_assert!( + self.request.is_some(), + "request must be set before calling enter_before_transmit_phase" + ); + self.request_checkpoint = try_clone(self.request().expect("checked above")); + self.phase = Phase::BeforeTransmit; + } - pub fn set_request(&mut self, request: Request) { - if self.request.is_some() { - panic!("Called set_request but a request was already set. This is a bug. Please report it."); - } + /// Advance to the Transmit phase. + #[doc(hidden)] + pub fn enter_transmit_phase(&mut self) { + debug!("entering \'transmit\' phase"); + debug_assert!( + self.phase.is_before_transmit(), + "called enter_transmit_phase but phase is not before transmit" + ); + self.phase = Phase::Transmit; + } - self.request = Some(request); + /// Advance to the BeforeDeserialization phase. + #[doc(hidden)] + pub fn enter_before_deserialization_phase(&mut self) { + debug!("entering \'before deserialization\' phase"); + debug_assert!( + self.phase.is_transmit(), + "called enter_before_deserialization_phase but phase is not 'transmit'" + ); + debug_assert!( + self.request.is_none(), + "request must be taken before entering the 'before deserialization' phase" + ); + debug_assert!( + self.response.is_some(), + "response must be set to before entering the 'before deserialization' phase" + ); + self.phase = Phase::BeforeDeserialization; } - pub fn set_response(&mut self, response: Response) { - if self.response.is_some() { - panic!("Called set_response but a transmit_response was already set. This is a bug. Please report it."); - } + /// Advance to the Deserialization phase. + #[doc(hidden)] + pub fn enter_deserialization_phase(&mut self) { + debug!("entering \'deserialization\' phase"); + debug_assert!( + self.phase.is_before_deserialization(), + "called enter_deserialization_phase but phase is not 'before deserialization'" + ); + self.phase = Phase::Deserialization; + } - self.response = Some(response); + /// Advance to the AfterDeserialization phase. + #[doc(hidden)] + pub fn enter_after_deserialization_phase(&mut self) { + debug!("entering \'after deserialization\' phase"); + debug_assert!( + self.phase.is_deserialization(), + "called enter_after_deserialization_phase but phase is not 'deserialization'" + ); + debug_assert!( + self.output_or_error.is_some(), + "output must be set to before entering the 'after deserialization' phase" + ); + self.phase = Phase::AfterDeserialization; } - pub fn set_output_or_error(&mut self, output: Result) { - if self.output_or_error.is_some() { - panic!( - "Called set_output but an output was already set. This is a bug. Please report it." - ); + /// Set the request checkpoint. This should only be called once, right before entering the retry loop. + #[doc(hidden)] + pub fn save_checkpoint(&mut self) { + trace!("saving request checkpoint..."); + self.request_checkpoint = self.request().and_then(try_clone); + match self.request_checkpoint.as_ref() { + Some(_) => trace!("successfully saved request checkpoint"), + None => trace!("failed to save request checkpoint: request body could not be cloned"), } + } - self.output_or_error = Some(output); + /// Returns false if rewinding isn't possible + #[doc(hidden)] + pub fn rewind(&mut self, _cfg: &mut ConfigBag) -> RewindResult { + // If request_checkpoint was never set, but we've already made one attempt, + // then this is not a retryable request + if self.request_checkpoint.is_none() && self.tainted { + return RewindResult::Impossible; + } + + if !self.tainted { + // The first call to rewind() happens before the request is ever touched, so we don't need + // to clone it then. However, the request must be marked as tainted so that subsequent calls + // to rewind() properly reload the saved request checkpoint. + self.tainted = true; + return RewindResult::Unnecessary; + } + + // Otherwise, rewind to the saved request checkpoint + self.phase = Phase::BeforeTransmit; + self.request = try_clone(self.request_checkpoint.as_ref().expect("checked above")); + assert!( + self.request.is_some(), + "if the request wasn't cloneable, then we should have already return from this method." + ); + self.response = None; + self.output_or_error = None; + RewindResult::Occurred } +} +impl InterceptorContext +where + E: Debug, +{ + /// Decomposes the context into its constituent parts. #[doc(hidden)] + #[allow(clippy::type_complexity)] pub fn into_parts( self, ) -> ( - Option, - Option, + Option, + Option>>, Option, Option, ) { @@ -146,4 +371,203 @@ impl InterceptorContext { self.response, ) } + + /// Convert this context into the final operation result that is returned in client's the public API. + #[doc(hidden)] + pub fn finalize(self) -> Result> { + let Self { + output_or_error, + response, + phase, + .. + } = self; + output_or_error + .expect("output_or_error must always be set before finalize is called.") + .map_err(|error| OrchestratorError::into_sdk_error(error, &phase, response)) + } + + /// Mark this context as failed due to errors during the operation. Any errors already contained + /// by the context will be replaced by the given error. + pub fn fail(&mut self, error: OrchestratorError) { + if !self.is_failed() { + trace!( + "orchestrator is transitioning to the 'failure' phase from the '{:?}' phase", + self.phase + ); + } + if let Some(Err(existing_err)) = mem::replace(&mut self.output_or_error, Some(Err(error))) { + error!("orchestrator context received an error but one was already present; Throwing away previous error: {:?}", existing_err); + } + } +} + +/// The result of attempting to rewind a request. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[doc(hidden)] +pub enum RewindResult { + /// The request couldn't be rewound because it wasn't cloneable. + Impossible, + /// The request wasn't rewound because it was unnecessary. + Unnecessary, + /// The request was rewound successfully. + Occurred, +} + +impl fmt::Display for RewindResult { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RewindResult::Impossible => write!( + f, + "The request couldn't be rewound because it wasn't cloneable." + ), + RewindResult::Unnecessary => { + write!(f, "The request wasn't rewound because it was unnecessary.") + } + RewindResult::Occurred => write!(f, "The request was rewound successfully."), + } + } +} + +fn try_clone(request: &HttpRequest) -> Option { + let cloned_body = request.body().try_clone()?; + let mut cloned_request = ::http::Request::builder() + .uri(request.uri().clone()) + .method(request.method()); + *cloned_request + .headers_mut() + .expect("builder has not been modified, headers must be valid") = request.headers().clone(); + Some( + cloned_request + .body(cloned_body) + .expect("a clone of a valid request should be a valid request"), + ) +} + +#[cfg(all(test, feature = "test-util"))] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use http::header::{AUTHORIZATION, CONTENT_LENGTH}; + use http::{HeaderValue, Uri}; + + #[test] + fn test_success_transitions() { + let input = Input::doesnt_matter(); + let output = Output::erase("output".to_string()); + + let mut context = InterceptorContext::new(input); + assert!(context.input().is_some()); + context.input_mut(); + + context.enter_serialization_phase(); + let _ = context.take_input(); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + context.enter_before_transmit_phase(); + context.request(); + context.request_mut(); + + context.enter_transmit_phase(); + let _ = context.take_request(); + context.set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + + context.enter_before_deserialization_phase(); + context.response(); + context.response_mut(); + + context.enter_deserialization_phase(); + context.response(); + context.response_mut(); + context.set_output_or_error(Ok(output)); + + context.enter_after_deserialization_phase(); + context.response(); + context.response_mut(); + let _ = context.output_or_error(); + let _ = context.output_or_error_mut(); + + let output = context.output_or_error.unwrap().expect("success"); + assert_eq!("output", output.downcast_ref::().unwrap()); + } + + #[test] + fn test_rewind_for_retry() { + let mut cfg = ConfigBag::base(); + let input = Input::doesnt_matter(); + let output = Output::erase("output".to_string()); + let error = Error::doesnt_matter(); + + let mut context = InterceptorContext::new(input); + assert!(context.input().is_some()); + + context.enter_serialization_phase(); + let _ = context.take_input(); + context.set_request( + http::Request::builder() + .header("test", "the-original-un-mutated-request") + .body(SdkBody::empty()) + .unwrap(), + ); + context.enter_before_transmit_phase(); + context.save_checkpoint(); + assert_eq!(context.rewind(&mut cfg), RewindResult::Unnecessary); + // Modify the test header post-checkpoint to simulate modifying the request for signing or a mutating interceptor + context.request_mut().unwrap().headers_mut().remove("test"); + context.request_mut().unwrap().headers_mut().insert( + "test", + HeaderValue::from_static("request-modified-after-signing"), + ); + + context.enter_transmit_phase(); + let request = context.take_request().unwrap(); + assert_eq!( + "request-modified-after-signing", + request.headers().get("test").unwrap() + ); + context.set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + + context.enter_before_deserialization_phase(); + context.enter_deserialization_phase(); + context.set_output_or_error(Err(OrchestratorError::operation(error))); + + assert_eq!(context.rewind(&mut cfg), RewindResult::Occurred); + + // Now after rewinding, the test header should be its original value + assert_eq!( + "the-original-un-mutated-request", + context.request().unwrap().headers().get("test").unwrap() + ); + + context.enter_transmit_phase(); + let _ = context.take_request(); + context.set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + + context.enter_before_deserialization_phase(); + context.enter_deserialization_phase(); + context.set_output_or_error(Ok(output)); + + context.enter_after_deserialization_phase(); + + let output = context.output_or_error.unwrap().expect("success"); + assert_eq!("output", output.downcast_ref::().unwrap()); + } + + #[test] + fn try_clone_clones_all_data() { + let request = ::http::Request::builder() + .uri(Uri::from_static("https://www.amazon.com")) + .method("POST") + .header(CONTENT_LENGTH, 456) + .header(AUTHORIZATION, "Token: hello") + .body(SdkBody::from("hello world!")) + .expect("valid request"); + let cloned = try_clone(&request).expect("request is cloneable"); + + assert_eq!(&Uri::from_static("https://www.amazon.com"), cloned.uri()); + assert_eq!("POST", cloned.method()); + assert_eq!(2, cloned.headers().len()); + assert_eq!("Token: hello", cloned.headers().get(AUTHORIZATION).unwrap(),); + assert_eq!("456", cloned.headers().get(CONTENT_LENGTH).unwrap()); + assert_eq!("hello world!".as_bytes(), cloned.body().bytes().unwrap()); + } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs new file mode 100644 index 0000000000..050aa1e7eb --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[derive(Debug)] +#[non_exhaustive] +pub(crate) enum Phase { + /// Represents the phase of an operation prior to serialization. + BeforeSerialization, + /// Represents the phase of an operation where the request is serialized. + Serialization, + /// Represents the phase of an operation prior to transmitting a request over the network. + BeforeTransmit, + /// Represents the phase of an operation where the request is transmitted over the network. + Transmit, + /// Represents the phase of an operation prior to parsing a response. + BeforeDeserialization, + /// Represents the phase of an operation where the response is parsed. + Deserialization, + /// Represents the phase of an operation after parsing a response. + AfterDeserialization, +} + +impl Phase { + pub(crate) fn is_before_serialization(&self) -> bool { + matches!(self, Self::BeforeSerialization) + } + + pub(crate) fn is_serialization(&self) -> bool { + matches!(self, Self::Serialization) + } + + pub(crate) fn is_before_transmit(&self) -> bool { + matches!(self, Self::BeforeTransmit) + } + + pub(crate) fn is_transmit(&self) -> bool { + matches!(self, Self::Transmit) + } + + pub(crate) fn is_before_deserialization(&self) -> bool { + matches!(self, Self::BeforeDeserialization) + } + + pub(crate) fn is_deserialization(&self) -> bool { + matches!(self, Self::Deserialization) + } + + pub(crate) fn is_after_deserialization(&self) -> bool { + matches!(self, Self::AfterDeserialization) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs new file mode 100644 index 0000000000..77f751e136 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -0,0 +1,303 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::{Error, Input, InterceptorContext, Output}; +use crate::client::interceptors::context::{Request, Response}; +use crate::client::orchestrator::OrchestratorError; +use std::fmt::Debug; + +macro_rules! impl_from_interceptor_context { + (ref $wrapper:ident) => { + impl<'a, I, O, E> From<&'a InterceptorContext> for $wrapper<'a, I, O, E> { + fn from(inner: &'a InterceptorContext) -> Self { + Self { inner } + } + } + }; + (mut $wrapper:ident) => { + impl<'a, I, O, E> From<&'a mut InterceptorContext> for $wrapper<'a, I, O, E> { + fn from(inner: &'a mut InterceptorContext) -> Self { + Self { inner } + } + } + }; +} + +macro_rules! expect { + ($self:ident, $what:ident) => { + $self.inner.$what().expect(concat!( + "`", + stringify!($what), + "` wasn't set in the underlying interceptor context. This is a bug." + )) + }; +} + +// +// BeforeSerializationInterceptorContextRef +// + +/// Interceptor context for the `read_before_execution` and `read_before_serialization` hooks. +/// +/// Only the input is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeSerializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, +} + +impl_from_interceptor_context!(ref BeforeSerializationInterceptorContextRef); + +impl<'a, I, O, E> BeforeSerializationInterceptorContextRef<'a, I, O, E> { + /// Returns a reference to the input. + pub fn input(&self) -> &I { + expect!(self, input) + } +} + +// +// BeforeSerializationInterceptorContextMut +// + +/// Interceptor context for the `modify_before_serialization` hook. +/// +/// Only the input is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeSerializationInterceptorContextMut<'a, I = Input, O = Output, E = Error> { + inner: &'a mut InterceptorContext, +} + +impl_from_interceptor_context!(mut BeforeSerializationInterceptorContextMut); + +impl<'a, I, O, E> BeforeSerializationInterceptorContextMut<'a, I, O, E> { + /// Returns a reference to the input. + pub fn input(&self) -> &I { + expect!(self, input) + } + + /// Returns a mutable reference to the input. + pub fn input_mut(&mut self) -> &mut I { + expect!(self, input_mut) + } +} + +// +// BeforeSerializationInterceptorContextRef +// + +/// Interceptor context for several hooks in between serialization and transmission. +/// +/// Only the request is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeTransmitInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, +} + +impl_from_interceptor_context!(ref BeforeTransmitInterceptorContextRef); + +impl<'a, I, O, E> BeforeTransmitInterceptorContextRef<'a, I, O, E> { + /// Returns a reference to the transmittable request for the operation being invoked. + pub fn request(&self) -> &Request { + expect!(self, request) + } +} + +// +// BeforeSerializationInterceptorContextMut +// + +/// Interceptor context for several hooks in between serialization and transmission. +/// +/// Only the request is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeTransmitInterceptorContextMut<'a, I = Input, O = Output, E = Error> { + inner: &'a mut InterceptorContext, +} + +impl_from_interceptor_context!(mut BeforeTransmitInterceptorContextMut); + +impl<'a, I, O, E> BeforeTransmitInterceptorContextMut<'a, I, O, E> { + /// Returns a reference to the transmittable request for the operation being invoked. + pub fn request(&self) -> &Request { + expect!(self, request) + } + + /// Returns a mutable reference to the transmittable request for the operation being invoked. + pub fn request_mut(&mut self) -> &mut Request { + expect!(self, request_mut) + } +} + +// +// BeforeDeserializationInterceptorContextRef +// + +/// Interceptor context for hooks before deserializing the response. +/// +/// Only the response is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeDeserializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, +} + +impl_from_interceptor_context!(ref BeforeDeserializationInterceptorContextRef); + +impl<'a, I, O, E> BeforeDeserializationInterceptorContextRef<'a, I, O, E> { + /// Returns a reference to the response. + pub fn response(&self) -> &Response { + expect!(self, response) + } +} + +// +// BeforeDeserializationInterceptorContextMut +// + +/// Interceptor context for hooks before deserializing the response. +/// +/// Only the response is available at this point in the operation. +pub struct BeforeDeserializationInterceptorContextMut<'a, I = Input, O = Output, E = Error> { + inner: &'a mut InterceptorContext, +} + +impl_from_interceptor_context!(mut BeforeDeserializationInterceptorContextMut); + +impl<'a, I, O, E> BeforeDeserializationInterceptorContextMut<'a, I, O, E> { + /// Returns a reference to the response. + pub fn response(&self) -> &Response { + expect!(self, response) + } + + /// Returns a mutable reference to the response. + pub fn response_mut(&mut self) -> &mut Response { + expect!(self, response_mut) + } + + #[doc(hidden)] + /// Downgrade this helper struct, returning the underlying InterceptorContext. There's no good + /// reason to use this unless you're writing tests or you have to interact with an API that + /// doesn't support the helper structs. + pub fn inner_mut(&mut self) -> &'_ mut InterceptorContext { + self.inner + } +} + +// +// AfterDeserializationInterceptorContextRef +// + +/// Interceptor context for hooks after deserializing the response. +/// +/// The response and the deserialized output or error are available at this point in the operation. +pub struct AfterDeserializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, +} + +impl_from_interceptor_context!(ref AfterDeserializationInterceptorContextRef); + +impl<'a, I, O, E> AfterDeserializationInterceptorContextRef<'a, I, O, E> { + /// Returns a reference to the response. + pub fn response(&self) -> &Response { + expect!(self, response) + } + + /// Returns a reference to the deserialized output or error. + pub fn output_or_error(&self) -> Result<&O, &OrchestratorError> { + expect!(self, output_or_error) + } +} + +// +// FinalizerInterceptorContextRef +// + +/// Interceptor context for finalization hooks. +/// +/// This context is used by the `read_after_attempt` and `read_after_execution` hooks +/// that are all called upon both success and failure, and may have varying levels +/// of context available depending on where a failure occurred if the operation failed. +pub struct FinalizerInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, +} + +impl_from_interceptor_context!(ref FinalizerInterceptorContextRef); + +impl<'a, I, O, E> FinalizerInterceptorContextRef<'a, I, O, E> { + /// Returns the operation input. + pub fn input(&self) -> Option<&I> { + self.inner.input.as_ref() + } + + /// Returns the serialized request. + pub fn request(&self) -> Option<&Request> { + self.inner.request.as_ref() + } + + /// Returns the raw response. + pub fn response(&self) -> Option<&Response> { + self.inner.response.as_ref() + } + + /// Returns the deserialized operation output or error. + pub fn output_or_error(&self) -> Option>> { + self.inner.output_or_error.as_ref().map(|o| o.as_ref()) + } +} + +// +// FinalizerInterceptorContextMut +// + +/// Interceptor context for finalization hooks. +/// +/// This context is used by the `modify_before_attempt_completion` and `modify_before_completion` hooks +/// that are all called upon both success and failure, and may have varying levels +/// of context available depending on where a failure occurred if the operation failed. +pub struct FinalizerInterceptorContextMut<'a, I = Input, O = Output, E = Error> { + inner: &'a mut InterceptorContext, +} + +impl_from_interceptor_context!(mut FinalizerInterceptorContextMut); + +impl<'a, I, O, E> FinalizerInterceptorContextMut<'a, I, O, E> { + /// Returns the operation input. + pub fn input(&self) -> Option<&I> { + self.inner.input.as_ref() + } + + /// Returns the serialized request. + pub fn request(&self) -> Option<&Request> { + self.inner.request.as_ref() + } + + /// Returns the raw response. + pub fn response(&self) -> Option<&Response> { + self.inner.response.as_ref() + } + + /// Returns the deserialized operation output or error. + pub fn output_or_error(&self) -> Option>> { + self.inner.output_or_error.as_ref().map(|o| o.as_ref()) + } + + /// Mutably returns the operation input. + pub fn input_mut(&mut self) -> Option<&mut I> { + self.inner.input.as_mut() + } + + /// Mutably returns the serialized request. + pub fn request_mut(&mut self) -> Option<&mut Request> { + self.inner.request.as_mut() + } + + /// Mutably returns the raw response. + pub fn response_mut(&mut self) -> Option<&mut Response> { + self.inner.response.as_mut() + } + + /// Mutably returns the deserialized operation output or error. + pub fn output_or_error_mut(&mut self) -> Option<&mut Result>> { + self.inner.output_or_error.as_mut() + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs index fc30027718..cba0829791 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs @@ -3,220 +3,75 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Errors related to smithy interceptors +//! Errors related to Smithy interceptors +use crate::box_error::BoxError; use std::fmt; -/// A generic error that behaves itself in async contexts -pub type BoxError = Box; +macro_rules! interceptor_error_fn { + ($fn_name:ident => $error_kind:ident (with source)) => { + #[doc = concat!("Create a new error indicating a failure with a ", stringify!($fn_name), " interceptor.")] + pub fn $fn_name( + interceptor_name: impl Into, + source: impl Into>, + ) -> Self { + Self { + kind: ErrorKind::$error_kind, + interceptor_name: Some(interceptor_name.into()), + source: Some(source.into()), + } + } + }; + ($fn_name:ident => $error_kind:ident (invalid $thing:ident access)) => { + #[doc = concat!("Create a new error indicating that an interceptor tried to access the ", stringify!($thing), " out of turn.")] + pub fn $fn_name() -> Self { + Self { + kind: ErrorKind::$error_kind, + interceptor_name: None, + source: None, + } + } + } +} -/// An error related to smithy interceptors. +/// An error related to Smithy interceptors. #[derive(Debug)] pub struct InterceptorError { kind: ErrorKind, + interceptor_name: Option, source: Option, } impl InterceptorError { - /// Create a new error indicating a failure withing a read_before_execution interceptor - pub fn read_before_execution( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeExecution, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_serialization interceptor - pub fn modify_before_serialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeSerialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_serialization interceptor - pub fn read_before_serialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeSerialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_serialization interceptor - pub fn read_after_serialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterSerialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_retry_loop interceptor - pub fn modify_before_retry_loop( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeRetryLoop, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_attempt interceptor - pub fn read_before_attempt( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeAttempt, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_signing interceptor - pub fn modify_before_signing( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeSigning, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_signing interceptor - pub fn read_before_signing( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeSigning, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_signing interceptor - pub fn read_after_signing( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterSigning, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_transmit interceptor - pub fn modify_before_transmit( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeTransmit, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_transmit interceptor - pub fn read_before_transmit( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeTransmit, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_transmit interceptor - pub fn read_after_transmit( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterTransmit, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_deserialization interceptor - pub fn modify_before_deserialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeDeserialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_deserialization interceptor - pub fn read_before_deserialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeDeserialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_deserialization interceptor - pub fn read_after_deserialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterDeserialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_attempt_completion interceptor - pub fn modify_before_attempt_completion( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeAttemptCompletion, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_attempt interceptor - pub fn read_after_attempt( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterAttempt, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_completion interceptor - pub fn modify_before_completion( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeCompletion, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_execution interceptor - pub fn read_after_execution( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterExecution, - source: Some(source.into()), - } - } - /// Create a new error indicating that an interceptor tried to access the request out of turn - pub fn invalid_request_access() -> Self { - Self { - kind: ErrorKind::InvalidRequestAccess, - source: None, - } - } - /// Create a new error indicating that an interceptor tried to access the response out of turn - pub fn invalid_response_access() -> Self { - Self { - kind: ErrorKind::InvalidResponseAccess, - source: None, - } - } - /// Create a new error indicating that an interceptor tried to access the input out of turn - pub fn invalid_input_access() -> Self { - Self { - kind: ErrorKind::InvalidInputAccess, - source: None, - } - } - /// Create a new error indicating that an interceptor tried to access the output out of turn - pub fn invalid_output_access() -> Self { - Self { - kind: ErrorKind::InvalidOutputAccess, - source: None, - } - } + interceptor_error_fn!(read_before_execution => ReadBeforeExecution (with source)); + interceptor_error_fn!(modify_before_serialization => ModifyBeforeSerialization (with source)); + interceptor_error_fn!(read_before_serialization => ReadBeforeSerialization (with source)); + interceptor_error_fn!(read_after_serialization => ReadAfterSerialization (with source)); + interceptor_error_fn!(modify_before_retry_loop => ModifyBeforeRetryLoop (with source)); + interceptor_error_fn!(read_before_attempt => ReadBeforeAttempt (with source)); + interceptor_error_fn!(modify_before_signing => ModifyBeforeSigning (with source)); + interceptor_error_fn!(read_before_signing => ReadBeforeSigning (with source)); + interceptor_error_fn!(read_after_signing => ReadAfterSigning (with source)); + interceptor_error_fn!(modify_before_transmit => ModifyBeforeTransmit (with source)); + interceptor_error_fn!(read_before_transmit => ReadBeforeTransmit (with source)); + interceptor_error_fn!(read_after_transmit => ReadAfterTransmit (with source)); + interceptor_error_fn!(modify_before_deserialization => ModifyBeforeDeserialization (with source)); + interceptor_error_fn!(read_before_deserialization => ReadBeforeDeserialization (with source)); + interceptor_error_fn!(read_after_deserialization => ReadAfterDeserialization (with source)); + interceptor_error_fn!(modify_before_attempt_completion => ModifyBeforeAttemptCompletion (with source)); + interceptor_error_fn!(read_after_attempt => ReadAfterAttempt (with source)); + interceptor_error_fn!(modify_before_completion => ModifyBeforeCompletion (with source)); + interceptor_error_fn!(read_after_execution => ReadAfterExecution (with source)); + + interceptor_error_fn!(modify_before_attempt_completion_failed => ModifyBeforeAttemptCompletion (with source)); + interceptor_error_fn!(read_after_attempt_failed => ReadAfterAttempt (with source)); + interceptor_error_fn!(modify_before_completion_failed => ModifyBeforeCompletion (with source)); + interceptor_error_fn!(read_after_execution_failed => ReadAfterExecution (with source)); + + interceptor_error_fn!(invalid_request_access => InvalidRequestAccess (invalid request access)); + interceptor_error_fn!(invalid_response_access => InvalidResponseAccess (invalid response access)); + interceptor_error_fn!(invalid_input_access => InvalidInputAccess (invalid input access)); + interceptor_error_fn!(invalid_output_access => InvalidOutputAccess (invalid output access)); } #[derive(Debug)] @@ -259,7 +114,6 @@ enum ErrorKind { ModifyBeforeCompletion, /// An error occurred within the read_after_execution interceptor ReadAfterExecution, - // There is no InvalidModeledRequestAccess because it's always accessible /// An interceptor tried to access the request out of turn InvalidRequestAccess, /// An interceptor tried to access the response out of turn @@ -270,81 +124,51 @@ enum ErrorKind { InvalidOutputAccess, } +macro_rules! display_interceptor_err { + ($self:ident, $f:ident, $(($error_kind:ident => $fn_name:ident ($($option:tt)+)),)+) => { + { + use ErrorKind::*; + match &$self.kind { + $($error_kind => display_interceptor_err!($self, $f, $fn_name, ($($option)+)),)+ + } + } + }; + ($self:ident, $f:ident, $fn_name:ident, (interceptor error)) => {{ + $f.write_str($self.interceptor_name.as_deref().unwrap_or_default())?; + $f.write_str(concat!(" ", stringify!($fn_name), " interceptor encountered an error")) + }}; + ($self:ident, $f:ident, $fn_name:ident, (invalid access $name:ident $message:literal)) => { + $f.write_str(concat!("tried to access the ", stringify!($name), " ", $message)) + }; +} + impl fmt::Display for InterceptorError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ErrorKind::*; - match &self.kind { - ReadBeforeExecution => { - write!(f, "read_before_execution interceptor encountered an error") - } - ModifyBeforeSerialization => write!( - f, - "modify_before_serialization interceptor encountered an error" - ), - ReadBeforeSerialization => write!( - f, - "read_before_serialization interceptor encountered an error" - ), - ReadAfterSerialization => write!( - f, - "read_after_serialization interceptor encountered an error" - ), - ModifyBeforeRetryLoop => write!( - f, - "modify_before_retry_loop interceptor encountered an error" - ), - ReadBeforeAttempt => write!(f, "read_before_attempt interceptor encountered an error"), - ModifyBeforeSigning => { - write!(f, "modify_before_signing interceptor encountered an error") - } - ReadBeforeSigning => write!(f, "read_before_signing interceptor encountered an error"), - ReadAfterSigning => write!(f, "read_after_signing interceptor encountered an error"), - ModifyBeforeTransmit => { - write!(f, "modify_before_transmit interceptor encountered an error") - } - ReadBeforeTransmit => { - write!(f, "read_before_transmit interceptor encountered an error") - } - ReadAfterTransmit => write!(f, "read_after_transmit interceptor encountered an error"), - ModifyBeforeDeserialization => write!( - f, - "modify_before_deserialization interceptor encountered an error" - ), - ReadBeforeDeserialization => write!( - f, - "read_before_deserialization interceptor encountered an error" - ), - ReadAfterDeserialization => write!( - f, - "read_after_deserialization interceptor encountered an error" - ), - ModifyBeforeAttemptCompletion => write!( - f, - "modify_before_attempt_completion interceptor encountered an error" - ), - ReadAfterAttempt => write!(f, "read_after_attempt interceptor encountered an error"), - ModifyBeforeCompletion => write!( - f, - "modify_before_completion interceptor encountered an error" - ), - ReadAfterExecution => { - write!(f, "read_after_execution interceptor encountered an error") - } - InvalidRequestAccess => { - write!(f, "tried to access request before request serialization") - } - InvalidResponseAccess => { - write!(f, "tried to access response before transmitting a request") - } - InvalidInputAccess => write!( - f, - "tried to access the input before response deserialization" - ), - InvalidOutputAccess => write!( - f, - "tried to access the output before response deserialization" - ), - } + display_interceptor_err!(self, f, + (ReadBeforeExecution => read_before_execution (interceptor error)), + (ModifyBeforeSerialization => modify_before_serialization (interceptor error)), + (ReadBeforeSerialization => read_before_serialization (interceptor error)), + (ReadAfterSerialization => read_after_serialization (interceptor error)), + (ModifyBeforeRetryLoop => modify_before_retry_loop (interceptor error)), + (ReadBeforeAttempt => read_Before_attempt (interceptor error)), + (ModifyBeforeSigning => modify_before_signing (interceptor error)), + (ReadBeforeSigning => read_before_signing (interceptor error)), + (ReadAfterSigning => read_after_signing (interceptor error)), + (ModifyBeforeTransmit => modify_before_transmit (interceptor error)), + (ReadBeforeTransmit => read_before_transmit (interceptor error)), + (ReadAfterTransmit => read_after_transmit (interceptor error)), + (ModifyBeforeDeserialization => modify_before_deserialization (interceptor error)), + (ReadBeforeDeserialization => read_before_deserialization (interceptor error)), + (ReadAfterDeserialization => read_after_deserialization (interceptor error)), + (ModifyBeforeAttemptCompletion => modify_before_attempt_completion (interceptor error)), + (ReadAfterAttempt => read_after_attempt (interceptor error)), + (ModifyBeforeCompletion => modify_before_completion (interceptor error)), + (ReadAfterExecution => read_after_execution (interceptor error)), + (InvalidRequestAccess => invalid_request_access (invalid access request "before request serialization")), + (InvalidResponseAccess => invalid_response_access (invalid access response "before transmitting a request")), + (InvalidInputAccess => invalid_input_access (invalid access input "after request serialization")), + (InvalidOutputAccess => invalid_output_access (invalid access output "before response deserialization")), + ) } } @@ -353,3 +177,32 @@ impl std::error::Error for InterceptorError { self.source.as_ref().map(|err| err.as_ref() as _) } } + +/// A convenience error that allows for adding additional `context` to `source` +#[derive(Debug)] +pub struct ContextAttachedError { + context: String, + source: Option, +} + +impl ContextAttachedError { + /// Creates a new `ContextAttachedError` with the given `context` and `source`. + pub fn new(context: impl Into, source: impl Into) -> Self { + Self { + context: context.into(), + source: Some(source.into()), + } + } +} + +impl fmt::Display for ContextAttachedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.context) + } +} + +impl std::error::Error for ContextAttachedError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.source.as_ref().map(|err| err.as_ref() as _) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index e38b284859..76f1bfadce 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -3,368 +3,252 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::identity::Identity; -use crate::client::interceptors::context::{Input, OutputOrError}; -use crate::client::interceptors::InterceptorContext; -use crate::config_bag::ConfigBag; -use crate::type_erasure::{TypeErasedBox, TypedBox}; +//! Client request orchestration. +//! +//! The orchestrator handles the full request/response lifecycle including: +//! - Request serialization +//! - Endpoint resolution +//! - Identity resolution +//! - Signing +//! - Request transmission with retry and timeouts +//! - Response deserialization +//! +//! There are several hook points in the orchestration where [interceptors](crate::client::interceptors) +//! can read and modify the input, request, response, or output/error. + +use crate::box_error::BoxError; +use crate::client::interceptors::context::phase::Phase; +use crate::client::interceptors::context::Error; +use crate::client::interceptors::InterceptorError; +use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_http::body::SdkBody; -use aws_smithy_http::property_bag::PropertyBag; -use std::any::Any; +use aws_smithy_http::result::{ConnectorError, SdkError}; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; +use bytes::Bytes; use std::fmt::Debug; -use std::future::Future; +use std::future::Future as StdFuture; use std::pin::Pin; -use std::sync::Arc; +/// Type alias for the HTTP request type that the orchestrator uses. pub type HttpRequest = http::Request; -pub type HttpResponse = http::Response; -pub type BoxError = Box; -pub type BoxFallibleFut = Pin>>>; - -pub trait TraceProbe: Send + Sync + Debug { - fn dispatch_events(&self) -> BoxFallibleFut<()>; -} - -pub trait RequestSerializer: Send + Sync + Debug { - fn serialize_input(&self, input: Input) -> Result; -} - -pub trait ResponseDeserializer: Send + Sync + Debug { - fn deserialize_streaming(&self, response: &mut HttpResponse) -> Option { - let _ = response; - None - } - - fn deserialize_nonstreaming(&self, response: &HttpResponse) -> OutputOrError; -} - -pub trait Connection: Send + Sync + Debug { - fn call(&self, request: &mut HttpRequest, cfg: &ConfigBag) -> BoxFallibleFut; -} - -pub trait RetryStrategy: Send + Sync + Debug { - fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result<(), BoxError>; - - fn should_attempt_retry( - &self, - context: &InterceptorContext, - cfg: &ConfigBag, - ) -> Result; -} -#[derive(Debug)] -pub struct AuthOptionResolverParams(TypeErasedBox); - -impl AuthOptionResolverParams { - pub fn new(params: T) -> Self { - Self(TypedBox::new(params).erase()) - } - - pub fn get(&self) -> Option<&T> { - self.0.downcast_ref() - } -} - -pub trait AuthOptionResolver: Send + Sync + Debug { - fn resolve_auth_options( - &self, - params: &AuthOptionResolverParams, - ) -> Result, BoxError>; -} +/// Type alias for the HTTP response type that the orchestrator uses. +pub type HttpResponse = http::Response; +/// Type alias for boxed futures that are returned from several traits since async trait functions are not stable yet (as of 2023-07-21). +/// +/// See [the Rust blog](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) for +/// more information on async functions in traits. +pub type BoxFuture = Pin> + Send>>; + +/// Type alias for futures that are returned from several traits since async trait functions are not stable yet (as of 2023-07-21). +/// +/// See [the Rust blog](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) for +/// more information on async functions in traits. +pub type Future = NowOrLater, BoxFuture>; + +/// Informs the orchestrator on whether or not the request body needs to be loaded into memory before transmit. +/// +/// This enum gets placed into the `ConfigBag` to change the orchestrator behavior. +/// Immediately after serialization (before the `read_after_serialization` interceptor hook), +/// if it was set to `Requested` in the config bag, it will be replaced back into the config bag as +/// `Loaded` with the request body contents for use in later interceptors. +/// +/// This all happens before the attempt loop, so the loaded request body will remain available +/// for interceptors that run in any subsequent retry attempts. +#[non_exhaustive] #[derive(Clone, Debug)] -pub struct HttpAuthOption { - scheme_id: &'static str, - properties: Arc, -} - -impl HttpAuthOption { - pub fn new(scheme_id: &'static str, properties: Arc) -> Self { - Self { - scheme_id, - properties, - } - } - - pub fn scheme_id(&self) -> &'static str { - self.scheme_id - } - - pub fn properties(&self) -> &PropertyBag { - &self.properties - } -} - -pub trait IdentityResolver: Send + Sync + Debug { - fn resolve_identity(&self, cfg: &ConfigBag) -> Result; -} - -#[derive(Debug)] -pub struct IdentityResolvers { - identity_resolvers: Vec<(&'static str, Box)>, +pub enum LoadedRequestBody { + /// Don't attempt to load the request body into memory. + NotNeeded, + /// Attempt to load the request body into memory. + Requested, + /// The request body is already loaded. + Loaded(Bytes), } -impl IdentityResolvers { - pub fn builder() -> builders::IdentityResolversBuilder { - builders::IdentityResolversBuilder::new() - } - - pub fn identity_resolver(&self, identity_type: &'static str) -> Option<&dyn IdentityResolver> { - self.identity_resolvers - .iter() - .find(|resolver| resolver.0 == identity_type) - .map(|resolver| &*resolver.1) - } +impl Storable for LoadedRequestBody { + type Storer = StoreReplace; } #[derive(Debug)] -struct HttpAuthSchemesInner { - schemes: Vec<(&'static str, Box)>, -} +enum ErrorKind { + /// An error occurred within an interceptor. + Interceptor { source: InterceptorError }, + /// An error returned by a service. + Operation { err: E }, + /// An error that occurs when a request times out. + Timeout { source: BoxError }, + /// An error that occurs when request dispatch fails. + Connector { source: ConnectorError }, + /// An error that occurs when a response can't be deserialized. + Response { source: BoxError }, + /// A general orchestrator error. + Other { source: BoxError }, +} + +/// Errors that can occur while running the orchestrator. #[derive(Debug)] -pub struct HttpAuthSchemes { - inner: Arc, -} - -impl HttpAuthSchemes { - pub fn builder() -> builders::HttpAuthSchemesBuilder { - Default::default() - } - - pub fn scheme(&self, name: &'static str) -> Option<&dyn HttpAuthScheme> { - self.inner - .schemes - .iter() - .find(|scheme| scheme.0 == name) - .map(|scheme| &*scheme.1) - } -} - -pub trait HttpAuthScheme: Send + Sync + Debug { - fn scheme_id(&self) -> &'static str; - - fn identity_resolver(&self, identity_resolvers: &IdentityResolvers) -> &dyn IdentityResolver; - - fn request_signer(&self) -> &dyn HttpRequestSigner; -} - -pub trait HttpRequestSigner: Send + Sync + Debug { - /// Return a signed version of the given request using the given identity. - /// - /// If the provided identity is incompatible with this signer, an error must be returned. - fn sign_request( - &self, - request: &HttpRequest, - identity: &Identity, - cfg: &ConfigBag, - ) -> Result; +pub struct OrchestratorError { + kind: ErrorKind, } -pub trait EndpointResolver: Send + Sync + Debug { - fn resolve_and_apply_endpoint(&self, request: &mut HttpRequest) -> Result<(), BoxError>; -} - -pub trait ConfigBagAccessors { - fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams; - fn set_auth_option_resolver_params( - &mut self, - auth_option_resolver_params: AuthOptionResolverParams, - ); - - fn auth_option_resolver(&self) -> &dyn AuthOptionResolver; - fn set_auth_option_resolver(&mut self, auth_option_resolver: impl AuthOptionResolver + 'static); - - fn endpoint_resolver(&self) -> &dyn EndpointResolver; - fn set_endpoint_resolver(&mut self, endpoint_resolver: impl EndpointResolver + 'static); - - fn identity_resolvers(&self) -> &IdentityResolvers; - fn set_identity_resolvers(&mut self, identity_resolvers: IdentityResolvers); - - fn connection(&self) -> &dyn Connection; - fn set_connection(&mut self, connection: impl Connection + 'static); - - fn http_auth_schemes(&self) -> &HttpAuthSchemes; - fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes); - - fn request_serializer(&self) -> &dyn RequestSerializer; - fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static); - - fn response_deserializer(&self) -> &dyn ResponseDeserializer; - fn set_response_deserializer( - &mut self, - response_serializer: impl ResponseDeserializer + 'static, - ); - - fn retry_strategy(&self) -> &dyn RetryStrategy; - fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static); - - fn trace_probe(&self) -> &dyn TraceProbe; - fn set_trace_probe(&mut self, trace_probe: impl TraceProbe + 'static); -} - -impl ConfigBagAccessors for ConfigBag { - fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams { - self.get::() - .expect("auth option resolver params must be set") - } - - fn set_auth_option_resolver_params( - &mut self, - auth_option_resolver_params: AuthOptionResolverParams, - ) { - self.put::(auth_option_resolver_params); - } - - fn auth_option_resolver(&self) -> &dyn AuthOptionResolver { - &**self - .get::>() - .expect("an auth option resolver must be set") - } - - fn set_auth_option_resolver( - &mut self, - auth_option_resolver: impl AuthOptionResolver + 'static, - ) { - self.put::>(Box::new(auth_option_resolver)); - } - - fn http_auth_schemes(&self) -> &HttpAuthSchemes { - self.get::() - .expect("auth schemes must be set") - } - - fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes) { - self.put::(http_auth_schemes); - } - - fn retry_strategy(&self) -> &dyn RetryStrategy { - &**self - .get::>() - .expect("a retry strategy must be set") +impl OrchestratorError { + /// Create a new `OrchestratorError` from the given source. + pub fn other(source: impl Into>) -> Self { + Self { + kind: ErrorKind::Other { + source: source.into(), + }, + } } - fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static) { - self.put::>(Box::new(retry_strategy)); + /// Create an operation error. + pub fn operation(err: E) -> Self { + Self { + kind: ErrorKind::Operation { err }, + } } - fn endpoint_resolver(&self) -> &dyn EndpointResolver { - &**self - .get::>() - .expect("an endpoint resolver must be set") + /// True if the underlying error is an operation error. + pub fn is_operation_error(&self) -> bool { + matches!(self.kind, ErrorKind::Operation { .. }) } - fn set_endpoint_resolver(&mut self, endpoint_resolver: impl EndpointResolver + 'static) { - self.put::>(Box::new(endpoint_resolver)); + /// Return this orchestrator error as an operation error if possible. + pub fn as_operation_error(&self) -> Option<&E> { + match &self.kind { + ErrorKind::Operation { err } => Some(err), + _ => None, + } } - fn identity_resolvers(&self) -> &IdentityResolvers { - self.get::() - .expect("identity resolvers must be configured") + /// Create an interceptor error with the given source. + pub fn interceptor(source: InterceptorError) -> Self { + Self { + kind: ErrorKind::Interceptor { source }, + } } - fn set_identity_resolvers(&mut self, identity_resolvers: IdentityResolvers) { - self.put::(identity_resolvers); + /// True if the underlying error is an interceptor error. + pub fn is_interceptor_error(&self) -> bool { + matches!(self.kind, ErrorKind::Interceptor { .. }) } - fn connection(&self) -> &dyn Connection { - &**self - .get::>() - .expect("missing connector") + /// Create a timeout error with the given source. + pub fn timeout(source: BoxError) -> Self { + Self { + kind: ErrorKind::Timeout { source }, + } } - fn set_connection(&mut self, connection: impl Connection + 'static) { - self.put::>(Box::new(connection)); + /// True if the underlying error is a timeout error. + pub fn is_timeout_error(&self) -> bool { + matches!(self.kind, ErrorKind::Timeout { .. }) } - fn request_serializer(&self) -> &dyn RequestSerializer { - &**self - .get::>() - .expect("missing request serializer") + /// Create a response error with the given source. + pub fn response(source: BoxError) -> Self { + Self { + kind: ErrorKind::Response { source }, + } } - fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static) { - self.put::>(Box::new(request_serializer)); + /// True if the underlying error is a response error. + pub fn is_response_error(&self) -> bool { + matches!(self.kind, ErrorKind::Response { .. }) } - fn response_deserializer(&self) -> &dyn ResponseDeserializer { - &**self - .get::>() - .expect("missing response deserializer") + /// Create a connector error with the given source. + pub fn connector(source: ConnectorError) -> Self { + Self { + kind: ErrorKind::Connector { source }, + } } - fn set_response_deserializer( - &mut self, - response_deserializer: impl ResponseDeserializer + 'static, - ) { - self.put::>(Box::new(response_deserializer)); + /// True if the underlying error is a [`ConnectorError`]. + pub fn is_connector_error(&self) -> bool { + matches!(self.kind, ErrorKind::Connector { .. }) } - fn trace_probe(&self) -> &dyn TraceProbe { - &**self - .get::>() - .expect("missing trace probe") + /// Return this orchestrator error as a connector error if possible. + pub fn as_connector_error(&self) -> Option<&ConnectorError> { + match &self.kind { + ErrorKind::Connector { source } => Some(source), + _ => None, + } } - fn set_trace_probe(&mut self, trace_probe: impl TraceProbe + 'static) { - self.put::>(Box::new(trace_probe)); + /// Convert the `OrchestratorError` into an [`SdkError`]. + pub(crate) fn into_sdk_error( + self, + phase: &Phase, + response: Option, + ) -> SdkError { + match self.kind { + ErrorKind::Interceptor { source } => { + use Phase::*; + match phase { + BeforeSerialization | Serialization => SdkError::construction_failure(source), + BeforeTransmit | Transmit => match response { + Some(response) => SdkError::response_error(source, response), + None => { + SdkError::dispatch_failure(ConnectorError::other(source.into(), None)) + } + }, + BeforeDeserialization | Deserialization | AfterDeserialization => { + SdkError::response_error(source, response.expect("phase has a response")) + } + } + } + ErrorKind::Operation { err } => { + debug_assert!(phase.is_after_deserialization(), "operation errors are a result of successfully receiving and parsing a response from the server. Therefore, we must be in the 'After Deserialization' phase."); + SdkError::service_error(err, response.expect("phase has a response")) + } + ErrorKind::Connector { source } => SdkError::dispatch_failure(source), + ErrorKind::Timeout { source } => SdkError::timeout_error(source), + ErrorKind::Response { source } => SdkError::response_error(source, response.unwrap()), + ErrorKind::Other { source } => { + use Phase::*; + match phase { + BeforeSerialization | Serialization => SdkError::construction_failure(source), + BeforeTransmit | Transmit => convert_dispatch_error(source, response), + BeforeDeserialization | Deserialization | AfterDeserialization => { + SdkError::response_error(source, response.expect("phase has a response")) + } + } + } + } } } -pub mod builders { - use super::*; - - #[derive(Debug, Default)] - pub struct IdentityResolversBuilder { - identity_resolvers: Vec<(&'static str, Box)>, - } - - impl IdentityResolversBuilder { - pub fn new() -> Self { - Default::default() - } - - pub fn identity_resolver( - mut self, - name: &'static str, - resolver: impl IdentityResolver + 'static, - ) -> Self { - self.identity_resolvers - .push((name, Box::new(resolver) as _)); - self - } - - pub fn build(self) -> IdentityResolvers { - IdentityResolvers { - identity_resolvers: self.identity_resolvers, - } +fn convert_dispatch_error( + err: BoxError, + response: Option, +) -> SdkError { + let err = match err.downcast::() { + Ok(connector_error) => { + return SdkError::dispatch_failure(*connector_error); } + Err(e) => e, + }; + match response { + Some(response) => SdkError::response_error(err, response), + None => SdkError::dispatch_failure(ConnectorError::other(err, None)), } +} - #[derive(Debug, Default)] - pub struct HttpAuthSchemesBuilder { - schemes: Vec<(&'static str, Box)>, +impl From for OrchestratorError +where + E: Debug + std::error::Error + 'static, +{ + fn from(err: InterceptorError) -> Self { + Self::interceptor(err) } +} - impl HttpAuthSchemesBuilder { - pub fn new() -> Self { - Default::default() - } - - pub fn auth_scheme( - mut self, - name: &'static str, - auth_scheme: impl HttpAuthScheme + 'static, - ) -> Self { - self.schemes.push((name, Box::new(auth_scheme) as _)); - self - } - - pub fn build(self) -> HttpAuthSchemes { - HttpAuthSchemes { - inner: Arc::new(HttpAuthSchemesInner { - schemes: self.schemes, - }), - } - } +impl From for OrchestratorError { + fn from(err: Error) -> Self { + Self::operation(err) } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index fd840a7bc9..0703e87d87 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -3,32 +3,234 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::interceptors::InterceptorContext; -use crate::client::orchestrator::{BoxError, RetryStrategy}; -use crate::config_bag::ConfigBag; -use aws_smithy_http::body::SdkBody; +//! Retry handling and token bucket. +//! +//! This code defines when and how failed requests should be retried. It also defines the behavior +//! used to limit the rate that requests are sent. -pub mod rate_limiting; +use crate::client::interceptors::context::InterceptorContext; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use std::fmt::Debug; +use std::time::Duration; +use tracing::trace; -#[derive(Debug, Clone)] -pub struct NeverRetryStrategy {} +pub use aws_smithy_types::retry::ErrorKind; -impl NeverRetryStrategy { - pub fn new() -> Self { - Self {} +#[derive(Debug, Clone, PartialEq, Eq)] +/// An answer to the question "should I make a request attempt?" +pub enum ShouldAttempt { + /// Yes, an attempt should be made + Yes, + /// No, no attempt should be made + No, + /// Yes, an attempt should be made, but only after the given amount of time has passed + YesAfterDelay(Duration), +} + +#[cfg(feature = "test-util")] +impl ShouldAttempt { + /// Returns the delay duration if this is a `YesAfterDelay` variant. + pub fn expect_delay(self) -> Duration { + match self { + ShouldAttempt::YesAfterDelay(delay) => delay, + _ => panic!("Expected this to be the `YesAfterDelay` variant but it was the `{self:?}` variant instead"), + } } } -impl RetryStrategy for NeverRetryStrategy { - fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result<(), BoxError> { - Ok(()) +/// Decider for whether or not to attempt a request, and when. +/// +/// The orchestrator consults the retry strategy every time before making a request. +/// This includes the initial request, and any retry attempts thereafter. The +/// orchestrator will retry indefinitely (until success) if the retry strategy +/// always returns `ShouldAttempt::Yes` from `should_attempt_retry`. +pub trait RetryStrategy: Send + Sync + Debug { + /// Decides if the initial attempt should be made. + fn should_attempt_initial_request( + &self, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result; + + /// Decides if a retry should be done. + /// + /// The previous attempt's output or error are provided in the + /// [`InterceptorContext`] when this is called. + /// + /// `ShouldAttempt::YesAfterDelay` can be used to add a backoff time. + fn should_attempt_retry( + &self, + context: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result; +} + +/// A shared retry strategy. +#[derive(Clone, Debug)] +pub struct SharedRetryStrategy(Arc); + +impl SharedRetryStrategy { + /// Creates a new [`SharedRetryStrategy`] from a retry strategy. + pub fn new(retry_strategy: impl RetryStrategy + 'static) -> Self { + Self(Arc::new(retry_strategy)) + } +} + +impl RetryStrategy for SharedRetryStrategy { + fn should_attempt_initial_request( + &self, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result { + self.0 + .should_attempt_initial_request(runtime_components, cfg) } fn should_attempt_retry( &self, - _context: &InterceptorContext, http::Response>, - _cfg: &ConfigBag, - ) -> Result { - Ok(false) + context: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result { + self.0 + .should_attempt_retry(context, runtime_components, cfg) } } + +/// Classification result from [`ClassifyRetry`]. +#[non_exhaustive] +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum RetryReason { + /// There was an unexpected error, and this is the kind of error so that it can be properly retried. + Error(ErrorKind), + /// The server explicitly told us to back off by this amount of time. + Explicit(Duration), +} + +/// Classifies what kind of retry is needed for a given an [`InterceptorContext`]. +pub trait ClassifyRetry: Send + Sync + Debug { + /// Run this classifier against an error to determine if it should be retried. Returns + /// `Some(RetryKind)` if the error should be retried; Otherwise returns `None`. + fn classify_retry(&self, ctx: &InterceptorContext) -> Option; + + /// The name that this classifier should report for debugging purposes. + fn name(&self) -> &'static str; +} + +/// Classifies an error into a [`RetryReason`]. +#[derive(Clone, Debug)] +pub struct RetryClassifiers { + inner: Vec>, +} + +impl RetryClassifiers { + /// Creates a new [`RetryClassifiers`]. + pub fn new() -> Self { + Self { + // It's always expected that at least one classifier will be defined, + // so we eagerly allocate for it. + inner: Vec::with_capacity(1), + } + } + + /// Adds a classifier to this collection. + pub fn with_classifier(mut self, retry_classifier: impl ClassifyRetry + 'static) -> Self { + self.inner.push(Arc::new(retry_classifier)); + self + } + + // TODO(https://github.com/awslabs/smithy-rs/issues/2632) make a map function so users can front-run or second-guess the classifier's decision + // pub fn map_classifiers(mut self, fun: Fn() -> RetryClassifiers) +} + +impl ClassifyRetry for RetryClassifiers { + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + // return the first non-None result + self.inner.iter().find_map(|cr| { + let maybe_reason = cr.classify_retry(ctx); + + match maybe_reason.as_ref() { + Some(reason) => trace!( + "\"{}\" classifier classified error as {:?}", + cr.name(), + reason + ), + None => trace!("\"{}\" classifier ignored the error", cr.name()), + }; + + maybe_reason + }) + } + + fn name(&self) -> &'static str { + "Collection of Classifiers" + } +} + +/// A type to track the number of requests sent by the orchestrator for a given operation. +/// +/// `RequestAttempts` is added to the `ConfigBag` by the orchestrator, +/// and holds the current attempt number. +#[derive(Debug, Clone, Copy)] +pub struct RequestAttempts { + attempts: u32, +} + +impl RequestAttempts { + /// Creates a new [`RequestAttempts`] with the given number of attempts. + pub fn new(attempts: u32) -> Self { + Self { attempts } + } + + /// Returns the number of attempts. + pub fn attempts(&self) -> u32 { + self.attempts + } +} + +impl From for RequestAttempts { + fn from(attempts: u32) -> Self { + Self::new(attempts) + } +} + +impl From for u32 { + fn from(value: RequestAttempts) -> Self { + value.attempts() + } +} + +impl Storable for RequestAttempts { + type Storer = StoreReplace; +} + +#[cfg(feature = "test-util")] +mod test_util { + use super::{ClassifyRetry, ErrorKind, RetryReason}; + use crate::client::interceptors::context::InterceptorContext; + use tracing::trace; + + /// A retry classifier for testing purposes. This classifier always returns + /// `Some(RetryReason::Error(ErrorKind))` where `ErrorKind` is the value provided when creating + /// this classifier. + #[derive(Debug)] + pub struct AlwaysRetry(pub ErrorKind); + + impl ClassifyRetry for AlwaysRetry { + fn classify_retry(&self, error: &InterceptorContext) -> Option { + trace!("Retrying error {:?} as an {:?}", error, self.0); + Some(RetryReason::Error(self.0)) + } + + fn name(&self) -> &'static str { + "Always Retry" + } + } +} + +use crate::box_error::BoxError; +use crate::client::runtime_components::RuntimeComponents; +use std::sync::Arc; +#[cfg(feature = "test-util")] +pub use test_util::AlwaysRetry; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting.rs deleted file mode 100644 index fcb6085fbd..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting.rs +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Code for rate-limiting smithy clients. - -pub mod error; -pub mod token; -pub mod token_bucket; - -pub use token::Token; -pub use token_bucket::TokenBucket; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/error.rs deleted file mode 100644 index b4f4d9821f..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/error.rs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Errors related to rate limiting - -use std::fmt; - -/// Errors related to a token bucket. -#[derive(Debug)] -pub struct RateLimitingError { - kind: ErrorKind, -} - -impl RateLimitingError { - /// An error that occurs when no tokens are left in the bucket. - pub fn no_tokens() -> Self { - Self { - kind: ErrorKind::NoTokens, - } - } - - /// An error that occurs due to a bug in the code. Please report bugs you encounter. - pub fn bug(s: impl ToString) -> Self { - Self { - kind: ErrorKind::Bug(s.to_string()), - } - } -} - -#[derive(Debug)] -enum ErrorKind { - /// A token was requested but there were no tokens left in the bucket. - NoTokens, - /// This error should never occur and is a bug. Please report it. - Bug(String), -} - -impl fmt::Display for RateLimitingError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ErrorKind::*; - match &self.kind { - NoTokens => write!(f, "No more tokens are left in the bucket."), - Bug(msg) => write!(f, "you've encountered a bug that needs reporting: {}", msg), - } - } -} - -impl std::error::Error for RateLimitingError {} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token.rs deleted file mode 100644 index 70d620e790..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Types and traits related to token buckets. Token buckets are used to limit the amount of -//! requests a client sends in order to avoid getting throttled. Token buckets can also act as a -//! form of concurrency control if a token is required to send a new request (as opposed to retry -//! requests only). - -use tokio::sync::OwnedSemaphorePermit; - -/// A trait implemented by types that represent a token dispensed from a [`TokenBucket`](super::TokenBucket). -pub trait Token { - /// Release this token back to the bucket. This should be called if the related request succeeds. - fn release(self); - - /// Forget this token, forever banishing it to the shadow realm, from whence no tokens return. - /// This should be called if the related request fails. - fn forget(self); -} - -/// The token type of [`Standard`]. -#[derive(Debug)] -pub struct Standard { - permit: Option, -} - -impl Standard { - pub(crate) fn new(permit: OwnedSemaphorePermit) -> Self { - Self { - permit: Some(permit), - } - } - - // Return an "empty" token for times when you need to return a token but there's no "cost" - // associated with an action. - pub(crate) fn empty() -> Self { - Self { permit: None } - } -} - -impl Token for Standard { - fn release(self) { - drop(self.permit) - } - - fn forget(self) { - if let Some(permit) = self.permit { - permit.forget() - } - } -} - -#[cfg(test)] -mod tests { - use super::Standard as Token; - use crate::client::retries::rate_limiting::token_bucket::Standard as TokenBucket; - - #[test] - fn token_bucket_trait_is_dyn_safe() { - let _tb: Box> = - Box::new(TokenBucket::builder().build()); - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token_bucket.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token_bucket.rs deleted file mode 100644 index 500ee723fd..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token_bucket.rs +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! A token bucket intended for use with the standard smithy client retry policy. - -use super::error::RateLimitingError; -use super::token; -use super::Token; -use aws_smithy_types::retry::{ErrorKind, RetryKind}; -use std::sync::Arc; -use tokio::sync::Semaphore; -use tokio::sync::TryAcquireError; - -/// The default number of tokens to start with -const STANDARD_INITIAL_RETRY_TOKENS: usize = 500; -/// The amount of tokens to remove from the bucket when a timeout error occurs -const STANDARD_TIMEOUT_ERROR_RETRY_COST: u32 = 10; -/// The amount of tokens to remove from the bucket when a throttling error occurs -const STANDARD_RETRYABLE_ERROR_RETRY_COST: u32 = 5; - -/// This trait is implemented by types that act as token buckets. Token buckets are used to regulate -/// the amount of requests sent by clients. Different token buckets may apply different strategies -/// to manage the number of tokens in a bucket. -/// -/// related: [`Token`], [`RateLimitingError`] -pub trait TokenBucket { - /// The type of tokens this bucket dispenses. - type Token: Token; - - /// Attempt to acquire a token from the bucket. This will fail if the bucket has no more tokens. - fn try_acquire( - &self, - previous_response_kind: Option, - ) -> Result; - - /// Get the number of available tokens in the bucket. - fn available(&self) -> usize; - - /// Refill the bucket with the given number of tokens. - fn refill(&self, tokens: usize); -} - -/// A token bucket implementation that uses a `tokio::sync::Semaphore` to track the number of tokens. -/// -/// - Whenever a request succeeds on the first try, `` token(s) -/// are added back to the bucket. -/// - When a request fails with a timeout error, `` token(s) -/// are removed from the bucket. -/// - When a request fails with a retryable error, `` token(s) -/// are removed from the bucket. -/// -/// The number of tokens in the bucket will always be >= `0` and <= ``. -#[derive(Clone, Debug)] -pub struct Standard { - inner: Arc, - max_tokens: usize, - timeout_error_cost: u32, - retryable_error_cost: u32, -} - -impl Standard { - /// Create a new `TokenBucket` using builder methods. - pub fn builder() -> Builder { - Builder::default() - } -} - -/// A builder for `TokenBucket`s. -#[derive(Default, Debug)] -pub struct Builder { - starting_tokens: Option, - max_tokens: Option, - timeout_error_cost: Option, - retryable_error_cost: Option, -} - -impl Builder { - /// The number of tokens the bucket will start with. Defaults to 500. - pub fn starting_tokens(mut self, starting_tokens: usize) -> Self { - self.starting_tokens = Some(starting_tokens); - self - } - - /// The maximum number of tokens that the bucket can hold. - /// Defaults to the value of `starting_tokens`. - pub fn max_tokens(mut self, max_tokens: usize) -> Self { - self.max_tokens = Some(max_tokens); - self - } - - /// How many tokens to remove from the bucket when a request fails due to a timeout error. - /// Defaults to 10. - pub fn timeout_error_cost(mut self, timeout_error_cost: u32) -> Self { - self.timeout_error_cost = Some(timeout_error_cost); - self - } - - /// How many tokens to remove from the bucket when a request fails due to a retryable error that - /// isn't timeout-related. Defaults to 5. - pub fn retryable_error_cost(mut self, retryable_error_cost: u32) -> Self { - self.retryable_error_cost = Some(retryable_error_cost); - self - } - - /// Build this builder. Unset fields will be set to their default values. - pub fn build(self) -> Standard { - let starting_tokens = self - .starting_tokens - .unwrap_or(STANDARD_INITIAL_RETRY_TOKENS); - let max_tokens = self.max_tokens.unwrap_or(starting_tokens); - let timeout_error_cost = self - .timeout_error_cost - .unwrap_or(STANDARD_TIMEOUT_ERROR_RETRY_COST); - let retryable_error_cost = self - .retryable_error_cost - .unwrap_or(STANDARD_RETRYABLE_ERROR_RETRY_COST); - - Standard { - inner: Arc::new(Semaphore::new(starting_tokens)), - max_tokens, - timeout_error_cost, - retryable_error_cost, - } - } -} - -impl TokenBucket for Standard { - type Token = token::Standard; - - fn try_acquire( - &self, - previous_response_kind: Option, - ) -> Result { - let number_of_tokens_to_acquire = match previous_response_kind { - None => { - // Return an empty token because the quota layer lifecycle expects a for each - // request even though the standard token bucket only requires tokens for retry - // attempts. - return Ok(token::Standard::empty()); - } - - Some(retry_kind) => match retry_kind { - RetryKind::Unnecessary => { - unreachable!("BUG: asked for a token to retry a successful request") - } - RetryKind::UnretryableFailure => { - unreachable!("BUG: asked for a token to retry an un-retryable request") - } - RetryKind::Explicit(_) => self.retryable_error_cost, - RetryKind::Error(error_kind) => match error_kind { - ErrorKind::ThrottlingError | ErrorKind::TransientError => { - self.timeout_error_cost - } - ErrorKind::ServerError => self.retryable_error_cost, - ErrorKind::ClientError => unreachable!( - "BUG: asked for a token to retry a request that failed due to user error" - ), - _ => unreachable!( - "A new variant '{:?}' was added to ErrorKind, please handle it", - error_kind - ), - }, - _ => unreachable!( - "A new variant '{:?}' was added to RetryKind, please handle it", - retry_kind - ), - }, - }; - - match self - .inner - .clone() - .try_acquire_many_owned(number_of_tokens_to_acquire) - { - Ok(permit) => Ok(token::Standard::new(permit)), - Err(TryAcquireError::NoPermits) => Err(RateLimitingError::no_tokens()), - Err(other) => Err(RateLimitingError::bug(other.to_string())), - } - } - - fn available(&self) -> usize { - self.inner.available_permits() - } - - fn refill(&self, tokens: usize) { - // Ensure the bucket doesn't overflow by limiting the amount of tokens to add, if necessary. - let amount_to_add = (self.available() + tokens).min(self.max_tokens) - self.available(); - if amount_to_add > 0 { - self.inner.add_permits(amount_to_add) - } - } -} - -#[cfg(test)] -mod test { - use super::{Token, TokenBucket}; - use super::{ - STANDARD_INITIAL_RETRY_TOKENS, STANDARD_RETRYABLE_ERROR_RETRY_COST, - STANDARD_TIMEOUT_ERROR_RETRY_COST, - }; - use aws_smithy_types::retry::{ErrorKind, RetryKind}; - - #[test] - fn bucket_works() { - let bucket = super::Standard::builder().build(); - assert_eq!(bucket.available(), STANDARD_INITIAL_RETRY_TOKENS); - - let token = bucket - .try_acquire(Some(RetryKind::Error(ErrorKind::ServerError))) - .unwrap(); - assert_eq!( - bucket.available(), - STANDARD_INITIAL_RETRY_TOKENS - STANDARD_RETRYABLE_ERROR_RETRY_COST as usize - ); - Box::new(token).release(); - - let token = bucket - .try_acquire(Some(RetryKind::Error(ErrorKind::TransientError))) - .unwrap(); - assert_eq!( - bucket.available(), - STANDARD_INITIAL_RETRY_TOKENS - STANDARD_TIMEOUT_ERROR_RETRY_COST as usize - ); - Box::new(token).forget(); - assert_eq!( - bucket.available(), - STANDARD_INITIAL_RETRY_TOKENS - STANDARD_TIMEOUT_ERROR_RETRY_COST as usize - ); - - bucket.refill(STANDARD_TIMEOUT_ERROR_RETRY_COST as usize); - assert_eq!(bucket.available(), STANDARD_INITIAL_RETRY_TOKENS); - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs new file mode 100644 index 0000000000..520c164e74 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs @@ -0,0 +1,799 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Runtime components used to make a request and handle a response. +//! +//! Runtime components are trait implementations that are _always_ used by the orchestrator. +//! There are other trait implementations that can be configured for a client, but if they +//! aren't directly and always used by the orchestrator, then they are placed in the +//! [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag) instead of in +//! [`RuntimeComponents`](RuntimeComponents). + +use crate::client::auth::{ + AuthScheme, AuthSchemeId, SharedAuthScheme, SharedAuthSchemeOptionResolver, +}; +use crate::client::connectors::SharedHttpConnector; +use crate::client::endpoint::SharedEndpointResolver; +use crate::client::identity::{ConfiguredIdentityResolver, SharedIdentityResolver}; +use crate::client::interceptors::SharedInterceptor; +use crate::client::retries::{RetryClassifiers, SharedRetryStrategy}; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_async::time::SharedTimeSource; +use std::fmt; + +pub(crate) static EMPTY_RUNTIME_COMPONENTS_BUILDER: RuntimeComponentsBuilder = + RuntimeComponentsBuilder::new("empty"); + +/// Internal to `declare_runtime_components!`. +/// +/// Merges a field from one builder into another. +macro_rules! merge { + (Option $other:ident . $name:ident => $self:ident) => { + $self.$name = $other.$name.clone().or($self.$name.take()); + }; + (Vec $other:ident . $name:ident => $self:ident) => { + if !$other.$name.is_empty() { + $self.$name.extend($other.$name.iter().cloned()); + } + }; +} +/// Internal to `declare_runtime_components!`. +/// +/// This is used when creating the builder's `build` method +/// to populate each individual field value. The `required`/`atLeastOneRequired` +/// validations are performed here. +macro_rules! builder_field_value { + (Option $self:ident . $name:ident) => { + $self.$name + }; + (Option $self:ident . $name:ident required) => { + $self.$name.ok_or(BuildError(concat!( + "the `", + stringify!($name), + "` runtime component is required" + )))? + }; + (Vec $self:ident . $name:ident) => { + $self.$name + }; + (Vec $self:ident . $name:ident atLeastOneRequired) => {{ + if $self.$name.is_empty() { + return Err(BuildError(concat!( + "at least one `", + stringify!($name), + "` runtime component is required" + ))); + } + $self.$name + }}; +} +/// Internal to `declare_runtime_components!`. +/// +/// Converts the field type from `Option` or `Vec` into `Option>` or `Vec>` respectively. +/// Also removes the `Option` wrapper for required fields in the non-builder struct. +macro_rules! runtime_component_field_type { + (Option $inner_type:ident) => { + Option> + }; + (Option $inner_type:ident required) => { + Tracked<$inner_type> + }; + (Vec $inner_type:ident) => { + Vec> + }; + (Vec $inner_type:ident atLeastOneRequired) => { + Vec> + }; +} +/// Internal to `declare_runtime_components!`. +/// +/// Converts an `$outer_type` into an empty instantiation for that type. +/// This is needed since `Default::default()` can't be used in a `const` function, +/// and `RuntimeComponentsBuilder::new()` is `const`. +macro_rules! empty_builder_value { + (Option) => { + None + }; + (Vec) => { + Vec::new() + }; +} + +/// Macro to define the structs for both `RuntimeComponents` and `RuntimeComponentsBuilder`. +/// +/// This is a macro in order to keep the fields consistent between the two, and to automatically +/// update the `merge_from` and `build` methods when new components are added. +/// +/// It also facilitates unit testing since the overall mechanism can be unit tested with different +/// fields that are easy to check in tests (testing with real components makes it hard +/// to tell that the correct component was selected when merging builders). +/// +/// # Example usage +/// +/// The two identifiers after "fields for" become the names of the struct and builder respectively. +/// Following that, all the fields are specified. Fields MUST be wrapped in `Option` or `Vec`. +/// To make a field required in the non-builder struct, add `#[required]` for `Option` fields, or +/// `#[atLeastOneRequired]` for `Vec` fields. +/// +/// ```no_compile +/// declare_runtime_components! { +/// fields for TestRc and TestRcBuilder { +/// some_optional_string: Option, +/// +/// some_optional_vec: Vec, +/// +/// #[required] +/// some_required_string: Option, +/// +/// #[atLeastOneRequired] +/// some_required_vec: Vec, +/// } +/// } +/// ``` +macro_rules! declare_runtime_components { + (fields for $rc_name:ident and $builder_name:ident { + $($(#[$option:ident])? $field_name:ident : $outer_type:ident<$inner_type:ident> ,)+ + }) => { + /// Components that can only be set in runtime plugins that the orchestrator uses directly to call an operation. + #[derive(Clone, Debug)] + pub struct $rc_name { + $($field_name: runtime_component_field_type!($outer_type $inner_type $($option)?),)+ + } + + /// Builder for [`RuntimeComponents`]. + #[derive(Clone, Debug)] + pub struct $builder_name { + builder_name: &'static str, + $($field_name: $outer_type>,)+ + } + impl $builder_name { + /// Creates a new builder. + /// + /// Since multiple builders are merged together to make the final [`RuntimeComponents`], + /// all components added by this builder are associated with the given `name` so that + /// the origin of a component can be easily found when debugging. + pub const fn new(name: &'static str) -> Self { + Self { + builder_name: name, + $($field_name: empty_builder_value!($outer_type),)+ + } + } + + /// Merge in components from another builder. + pub fn merge_from(mut self, other: &Self) -> Self { + $(merge!($outer_type other.$field_name => self);)+ + self + } + + /// Builds [`RuntimeComponents`] from this builder. + pub fn build(self) -> Result<$rc_name, BuildError> { + Ok($rc_name { + $($field_name: builder_field_value!($outer_type self.$field_name $($option)?),)+ + }) + } + } + }; +} + +declare_runtime_components! { + fields for RuntimeComponents and RuntimeComponentsBuilder { + #[required] + auth_scheme_option_resolver: Option, + + // A connector is not required since a client could technically only be used for presigning + http_connector: Option, + + #[required] + endpoint_resolver: Option, + + #[atLeastOneRequired] + auth_schemes: Vec, + + #[atLeastOneRequired] + identity_resolvers: Vec, + + interceptors: Vec, + + retry_classifiers: Option, + + #[required] + retry_strategy: Option, + + time_source: Option, + + sleep_impl: Option, + } +} + +impl RuntimeComponents { + /// Returns a builder for runtime components. + pub fn builder(name: &'static str) -> RuntimeComponentsBuilder { + RuntimeComponentsBuilder::new(name) + } + + /// Returns the auth scheme option resolver. + pub fn auth_scheme_option_resolver(&self) -> SharedAuthSchemeOptionResolver { + self.auth_scheme_option_resolver.value.clone() + } + + /// Returns the connector. + pub fn http_connector(&self) -> Option { + self.http_connector.as_ref().map(|s| s.value.clone()) + } + + /// Returns the endpoint resolver. + pub fn endpoint_resolver(&self) -> SharedEndpointResolver { + self.endpoint_resolver.value.clone() + } + + /// Returns the requested auth scheme if it is set. + pub fn auth_scheme(&self, scheme_id: AuthSchemeId) -> Option { + self.auth_schemes + .iter() + .find(|s| s.value.scheme_id() == scheme_id) + .map(|s| s.value.clone()) + } + + /// Returns an iterator over the interceptors. + pub fn interceptors(&self) -> impl Iterator + '_ { + self.interceptors.iter().map(|s| s.value.clone()) + } + + /// Returns the retry classifiers. + pub fn retry_classifiers(&self) -> Option<&RetryClassifiers> { + self.retry_classifiers.as_ref().map(|s| &s.value) + } + + /// Returns the retry strategy. + pub fn retry_strategy(&self) -> SharedRetryStrategy { + self.retry_strategy.value.clone() + } + + /// Returns the async sleep implementation. + pub fn sleep_impl(&self) -> Option { + self.sleep_impl.as_ref().map(|s| s.value.clone()) + } + + /// Returns the time source. + pub fn time_source(&self) -> Option { + self.time_source.as_ref().map(|s| s.value.clone()) + } +} + +impl RuntimeComponentsBuilder { + /// Returns the auth scheme option resolver. + pub fn auth_scheme_option_resolver(&self) -> Option { + self.auth_scheme_option_resolver + .as_ref() + .map(|s| s.value.clone()) + } + + /// Sets the auth scheme option resolver. + pub fn set_auth_scheme_option_resolver( + &mut self, + auth_scheme_option_resolver: Option, + ) -> &mut Self { + self.auth_scheme_option_resolver = + auth_scheme_option_resolver.map(|r| Tracked::new(self.builder_name, r)); + self + } + + /// Sets the auth scheme option resolver. + pub fn with_auth_scheme_option_resolver( + mut self, + auth_scheme_option_resolver: Option, + ) -> Self { + self.set_auth_scheme_option_resolver(auth_scheme_option_resolver); + self + } + + /// Returns the HTTP connector. + pub fn http_connector(&self) -> Option { + self.http_connector.as_ref().map(|s| s.value.clone()) + } + + /// Sets the HTTP connector. + pub fn set_http_connector(&mut self, connector: Option) -> &mut Self { + self.http_connector = connector.map(|c| Tracked::new(self.builder_name, c)); + self + } + + /// Sets the HTTP connector. + pub fn with_http_connector(mut self, connector: Option) -> Self { + self.set_http_connector(connector); + self + } + + /// Returns the endpoint resolver. + pub fn endpoint_resolver(&self) -> Option { + self.endpoint_resolver.as_ref().map(|s| s.value.clone()) + } + + /// Sets the endpoint resolver. + pub fn set_endpoint_resolver( + &mut self, + endpoint_resolver: Option, + ) -> &mut Self { + self.endpoint_resolver = endpoint_resolver.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the endpoint resolver. + pub fn with_endpoint_resolver( + mut self, + endpoint_resolver: Option, + ) -> Self { + self.set_endpoint_resolver(endpoint_resolver); + self + } + + /// Returns the auth schemes. + pub fn auth_schemes(&self) -> impl Iterator + '_ { + self.auth_schemes.iter().map(|s| s.value.clone()) + } + + /// Adds an auth scheme. + pub fn push_auth_scheme(&mut self, auth_scheme: SharedAuthScheme) -> &mut Self { + self.auth_schemes + .push(Tracked::new(self.builder_name, auth_scheme)); + self + } + + /// Adds an auth scheme. + pub fn with_auth_scheme(mut self, auth_scheme: SharedAuthScheme) -> Self { + self.push_auth_scheme(auth_scheme); + self + } + + /// Adds an identity resolver. + pub fn push_identity_resolver( + &mut self, + scheme_id: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) -> &mut Self { + self.identity_resolvers.push(Tracked::new( + self.builder_name, + ConfiguredIdentityResolver::new(scheme_id, identity_resolver), + )); + self + } + + /// Adds an identity resolver. + pub fn with_identity_resolver( + mut self, + scheme_id: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) -> Self { + self.push_identity_resolver(scheme_id, identity_resolver); + self + } + + /// Returns the interceptors. + pub fn interceptors(&self) -> impl Iterator + '_ { + self.interceptors.iter().map(|s| s.value.clone()) + } + + /// Adds all the given interceptors. + pub fn extend_interceptors( + &mut self, + interceptors: impl Iterator, + ) -> &mut Self { + self.interceptors + .extend(interceptors.map(|s| Tracked::new(self.builder_name, s))); + self + } + + /// Adds an interceptor. + pub fn push_interceptor(&mut self, interceptor: SharedInterceptor) -> &mut Self { + self.interceptors + .push(Tracked::new(self.builder_name, interceptor)); + self + } + + /// Adds an interceptor. + pub fn with_interceptor(mut self, interceptor: SharedInterceptor) -> Self { + self.push_interceptor(interceptor); + self + } + + /// Directly sets the interceptors and clears out any that were previously pushed. + pub fn set_interceptors( + &mut self, + interceptors: impl Iterator, + ) -> &mut Self { + self.interceptors.clear(); + self.interceptors + .extend(interceptors.map(|s| Tracked::new(self.builder_name, s))); + self + } + + /// Directly sets the interceptors and clears out any that were previously pushed. + pub fn with_interceptors( + mut self, + interceptors: impl Iterator, + ) -> Self { + self.set_interceptors(interceptors); + self + } + + /// Returns the retry classifiers. + pub fn retry_classifiers(&self) -> Option<&RetryClassifiers> { + self.retry_classifiers.as_ref().map(|s| &s.value) + } + + /// Sets the retry classifiers. + pub fn set_retry_classifiers( + &mut self, + retry_classifiers: Option, + ) -> &mut Self { + self.retry_classifiers = retry_classifiers.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the retry classifiers. + pub fn with_retry_classifiers(mut self, retry_classifiers: Option) -> Self { + self.retry_classifiers = retry_classifiers.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Returns the retry strategy. + pub fn retry_strategy(&self) -> Option { + self.retry_strategy.as_ref().map(|s| s.value.clone()) + } + + /// Sets the retry strategy. + pub fn set_retry_strategy(&mut self, retry_strategy: Option) -> &mut Self { + self.retry_strategy = retry_strategy.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the retry strategy. + pub fn with_retry_strategy(mut self, retry_strategy: Option) -> Self { + self.retry_strategy = retry_strategy.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Returns the async sleep implementation. + pub fn sleep_impl(&self) -> Option { + self.sleep_impl.as_ref().map(|s| s.value.clone()) + } + + /// Sets the async sleep implementation. + pub fn set_sleep_impl(&mut self, sleep_impl: Option) -> &mut Self { + self.sleep_impl = sleep_impl.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the async sleep implementation. + pub fn with_sleep_impl(mut self, sleep_impl: Option) -> Self { + self.sleep_impl = sleep_impl.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Returns the time source. + pub fn time_source(&self) -> Option { + self.time_source.as_ref().map(|s| s.value.clone()) + } + + /// Sets the time source. + pub fn set_time_source(&mut self, time_source: Option) -> &mut Self { + self.time_source = time_source.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the time source. + pub fn with_time_source(mut self, time_source: Option) -> Self { + self.time_source = time_source.map(|s| Tracked::new(self.builder_name, s)); + self + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(test, derive(Eq, PartialEq))] +struct Tracked { + _origin: &'static str, + value: T, +} + +impl Tracked { + fn new(origin: &'static str, value: T) -> Self { + Self { + _origin: origin, + value, + } + } +} + +impl RuntimeComponentsBuilder { + /// Creates a runtime components builder with all the required components filled in with fake (panicking) implementations. + #[cfg(feature = "test-util")] + pub fn for_tests() -> Self { + use crate::client::auth::AuthSchemeOptionResolver; + use crate::client::connectors::HttpConnector; + use crate::client::endpoint::{EndpointResolver, EndpointResolverParams}; + use crate::client::identity::Identity; + use crate::client::identity::IdentityResolver; + use crate::client::orchestrator::Future; + use crate::client::retries::RetryStrategy; + use aws_smithy_async::rt::sleep::AsyncSleep; + use aws_smithy_async::time::TimeSource; + use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::endpoint::Endpoint; + + #[derive(Debug)] + struct FakeAuthSchemeOptionResolver; + impl AuthSchemeOptionResolver for FakeAuthSchemeOptionResolver { + fn resolve_auth_scheme_options( + &self, + _: &crate::client::auth::AuthSchemeOptionResolverParams, + ) -> Result, crate::box_error::BoxError> + { + unreachable!("fake auth scheme option resolver must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeConnector; + impl HttpConnector for FakeConnector { + fn call( + &self, + _: crate::client::orchestrator::HttpRequest, + ) -> crate::client::orchestrator::BoxFuture + { + unreachable!("fake connector must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeEndpointResolver; + impl EndpointResolver for FakeEndpointResolver { + fn resolve_endpoint(&self, _: &EndpointResolverParams) -> Future { + unreachable!("fake endpoint resolver must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeAuthScheme; + impl AuthScheme for FakeAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + AuthSchemeId::new("fake") + } + + fn identity_resolver( + &self, + _: &dyn GetIdentityResolver, + ) -> Option { + None + } + + fn signer(&self) -> &dyn crate::client::auth::Signer { + unreachable!("fake http auth scheme must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeIdentityResolver; + impl IdentityResolver for FakeIdentityResolver { + fn resolve_identity(&self, _: &ConfigBag) -> Future { + unreachable!("fake identity resolver must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeRetryStrategy; + impl RetryStrategy for FakeRetryStrategy { + fn should_attempt_initial_request( + &self, + _: &RuntimeComponents, + _: &ConfigBag, + ) -> Result + { + unreachable!("fake retry strategy must be overridden for this test") + } + + fn should_attempt_retry( + &self, + _: &crate::client::interceptors::context::InterceptorContext, + _: &RuntimeComponents, + _: &ConfigBag, + ) -> Result + { + unreachable!("fake retry strategy must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeTimeSource; + impl TimeSource for FakeTimeSource { + fn now(&self) -> std::time::SystemTime { + unreachable!("fake time source must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeSleep; + impl AsyncSleep for FakeSleep { + fn sleep(&self, _: std::time::Duration) -> aws_smithy_async::rt::sleep::Sleep { + unreachable!("fake sleep must be overridden for this test") + } + } + + Self::new("aws_smithy_runtime_api::client::runtime_components::RuntimeComponentBuilder::for_tests") + .with_auth_scheme(SharedAuthScheme::new(FakeAuthScheme)) + .with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new(FakeAuthSchemeOptionResolver))) + .with_endpoint_resolver(Some(SharedEndpointResolver::new(FakeEndpointResolver))) + .with_http_connector(Some(SharedHttpConnector::new(FakeConnector))) + .with_identity_resolver(AuthSchemeId::new("fake"), SharedIdentityResolver::new(FakeIdentityResolver)) + .with_retry_classifiers(Some(RetryClassifiers::new())) + .with_retry_strategy(Some(SharedRetryStrategy::new(FakeRetryStrategy))) + .with_sleep_impl(Some(SharedAsyncSleep::new(FakeSleep))) + .with_time_source(Some(SharedTimeSource::new(FakeTimeSource))) + } +} + +/// An error that occurs when building runtime components. +#[derive(Debug)] +pub struct BuildError(&'static str); + +impl std::error::Error for BuildError {} + +impl fmt::Display for BuildError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// A trait for retrieving a shared identity resolver. +/// +/// This trait exists so that [`AuthScheme::identity_resolver`](crate::client::auth::AuthScheme::identity_resolver) +/// can have access to configured identity resolvers without having access to all the runtime components. +pub trait GetIdentityResolver: Send + Sync { + /// Returns the requested identity resolver if it is set. + fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option; +} + +impl GetIdentityResolver for RuntimeComponents { + fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option { + self.identity_resolvers + .iter() + .find(|s| s.value.scheme_id() == scheme_id) + .map(|s| s.value.identity_resolver()) + } +} + +#[cfg(all(test, feature = "test-util"))] +mod tests { + use super::*; + + #[test] + #[allow(unreachable_pub)] + #[allow(dead_code)] + fn the_builders_should_merge() { + declare_runtime_components! { + fields for TestRc and TestRcBuilder { + #[required] + some_required_string: Option, + + some_optional_string: Option, + + #[atLeastOneRequired] + some_required_vec: Vec, + + some_optional_vec: Vec, + } + } + + let builder1 = TestRcBuilder { + builder_name: "builder1", + some_required_string: Some(Tracked::new("builder1", "override_me".into())), + some_optional_string: Some(Tracked::new("builder1", "override_me optional".into())), + some_required_vec: vec![Tracked::new("builder1", "first".into())], + some_optional_vec: vec![Tracked::new("builder1", "first optional".into())], + }; + let builder2 = TestRcBuilder { + builder_name: "builder2", + some_required_string: Some(Tracked::new("builder2", "override_me_too".into())), + some_optional_string: Some(Tracked::new("builder2", "override_me_too optional".into())), + some_required_vec: vec![Tracked::new("builder2", "second".into())], + some_optional_vec: vec![Tracked::new("builder2", "second optional".into())], + }; + let builder3 = TestRcBuilder { + builder_name: "builder3", + some_required_string: Some(Tracked::new("builder3", "correct".into())), + some_optional_string: Some(Tracked::new("builder3", "correct optional".into())), + some_required_vec: vec![Tracked::new("builder3", "third".into())], + some_optional_vec: vec![Tracked::new("builder3", "third optional".into())], + }; + let rc = TestRcBuilder::new("root") + .merge_from(&builder1) + .merge_from(&builder2) + .merge_from(&builder3) + .build() + .expect("success"); + assert_eq!( + Tracked::new("builder3", "correct".to_string()), + rc.some_required_string + ); + assert_eq!( + Some(Tracked::new("builder3", "correct optional".to_string())), + rc.some_optional_string + ); + assert_eq!( + vec![ + Tracked::new("builder1", "first".to_string()), + Tracked::new("builder2", "second".into()), + Tracked::new("builder3", "third".into()) + ], + rc.some_required_vec + ); + assert_eq!( + vec![ + Tracked::new("builder1", "first optional".to_string()), + Tracked::new("builder2", "second optional".into()), + Tracked::new("builder3", "third optional".into()) + ], + rc.some_optional_vec + ); + } + + #[test] + #[allow(unreachable_pub)] + #[allow(dead_code)] + #[should_panic(expected = "the `_some_string` runtime component is required")] + fn require_field_singular() { + declare_runtime_components! { + fields for TestRc and TestRcBuilder { + #[required] + _some_string: Option, + } + } + + let rc = TestRcBuilder::new("test").build().unwrap(); + + // Ensure the correct types were used + let _: Tracked = rc._some_string; + } + + #[test] + #[allow(unreachable_pub)] + #[allow(dead_code)] + #[should_panic(expected = "at least one `_some_vec` runtime component is required")] + fn require_field_plural() { + declare_runtime_components! { + fields for TestRc and TestRcBuilder { + #[atLeastOneRequired] + _some_vec: Vec, + } + } + + let rc = TestRcBuilder::new("test").build().unwrap(); + + // Ensure the correct types were used + let _: Vec> = rc._some_vec; + } + + #[test] + #[allow(unreachable_pub)] + #[allow(dead_code)] + fn optional_fields_dont_panic() { + declare_runtime_components! { + fields for TestRc and TestRcBuilder { + _some_optional_string: Option, + _some_optional_vec: Vec, + } + } + + let rc = TestRcBuilder::new("test").build().unwrap(); + + // Ensure the correct types were used + let _: Option> = rc._some_optional_string; + let _: Vec> = rc._some_optional_vec; + } + + #[test] + fn building_test_builder_should_not_panic() { + let _ = RuntimeComponentsBuilder::for_tests().build(); // should not panic + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 8b262a453e..abc687ca66 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -3,27 +3,128 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::config_bag::ConfigBag; +//! Runtime plugin type definitions. +//! +//! Runtime plugins are used to extend the runtime with custom behavior. +//! This can include: +//! - Registering interceptors +//! - Registering auth schemes +//! - Adding entries to the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag) for orchestration +//! - Setting runtime components +//! +//! Runtime plugins are divided into service/operation "levels", with service runtime plugins +//! executing before operation runtime plugins. Runtime plugins configured in a service +//! config will always be at the service level, while runtime plugins added during +//! operation customization will be at the operation level. Custom runtime plugins will +//! always run after the default runtime plugins within their level. + +use crate::box_error::BoxError; +use crate::client::runtime_components::{ + RuntimeComponentsBuilder, EMPTY_RUNTIME_COMPONENTS_BUILDER, +}; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer}; +use std::borrow::Cow; +use std::fmt::Debug; +use std::sync::Arc; + +/// Runtime plugin trait +/// +/// A `RuntimePlugin` is the unit of configuration for augmenting the SDK with new behavior. +/// +/// Runtime plugins can register interceptors, set runtime components, and modify configuration. +pub trait RuntimePlugin: Debug + Send + Sync { + /// Optionally returns additional config that should be added to the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag). + /// + /// As a best practice, a frozen layer should be stored on the runtime plugin instance as + /// a member, and then cloned upon return since that clone is cheap. Constructing a new + /// [`Layer`](aws_smithy_types::config_bag::Layer) and freezing it will require a lot of allocations. + fn config(&self) -> Option { + None + } + + /// Returns a [`RuntimeComponentsBuilder`](RuntimeComponentsBuilder) to incorporate into the final runtime components. + /// + /// The order of runtime plugins determines which runtime components "win". Components set by later runtime plugins will + /// override those set by earlier runtime plugins. + /// + /// If no runtime component changes are desired, just return an empty builder. + /// + /// This method returns a [`Cow`] for flexibility. Some implementers may want to store the components builder + /// as a member and return a reference to it, while others may need to create the builder every call. If possible, + /// returning a reference is preferred for performance. + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&EMPTY_RUNTIME_COMPONENTS_BUILDER) + } +} + +/// Shared runtime plugin +/// +/// Allows for multiple places to share ownership of one runtime plugin. +#[derive(Debug, Clone)] +pub struct SharedRuntimePlugin(Arc); + +impl SharedRuntimePlugin { + /// Returns a new [`SharedRuntimePlugin`]. + pub fn new(plugin: impl RuntimePlugin + 'static) -> Self { + Self(Arc::new(plugin)) + } +} + +impl RuntimePlugin for SharedRuntimePlugin { + fn config(&self) -> Option { + self.0.config() + } + + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + self.0.runtime_components() + } +} + +/// Runtime plugin that simply returns the config and components given at construction time. +#[derive(Default, Debug)] +pub struct StaticRuntimePlugin { + config: Option, + runtime_components: Option, +} + +impl StaticRuntimePlugin { + /// Returns a new [`StaticRuntimePlugin`]. + pub fn new() -> Self { + Default::default() + } -pub type BoxError = Box; + /// Changes the config. + pub fn with_config(mut self, config: FrozenLayer) -> Self { + self.config = Some(config); + self + } -pub trait RuntimePlugin { - fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError>; + /// Changes the runtime components. + pub fn with_runtime_components(mut self, runtime_components: RuntimeComponentsBuilder) -> Self { + self.runtime_components = Some(runtime_components); + self + } } -impl From for Box -where - T: RuntimePlugin + 'static, -{ - fn from(t: T) -> Self { - Box::new(t) as _ +impl RuntimePlugin for StaticRuntimePlugin { + fn config(&self) -> Option { + self.config.clone() + } + + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + self.runtime_components + .as_ref() + .map(Cow::Borrowed) + .unwrap_or_else(|| RuntimePlugin::runtime_components(self)) } } -#[derive(Default)] +/// Used internally in the orchestrator implementation and in the generated code. Not intended to be used elsewhere. +#[doc(hidden)] +#[derive(Default, Clone, Debug)] pub struct RuntimePlugins { - client_plugins: Vec>, - operation_plugins: Vec>, + client_plugins: Vec, + operation_plugins: Vec, } impl RuntimePlugins { @@ -31,54 +132,65 @@ impl RuntimePlugins { Default::default() } - pub fn with_client_plugin( - mut self, - plugin: impl Into>, - ) -> Self { - self.client_plugins.push(plugin.into()); + pub fn with_client_plugin(mut self, plugin: impl RuntimePlugin + 'static) -> Self { + self.client_plugins.push(SharedRuntimePlugin::new(plugin)); self } - pub fn with_operation_plugin( - mut self, - plugin: impl Into>, - ) -> Self { - self.operation_plugins.push(plugin.into()); + pub fn with_operation_plugin(mut self, plugin: impl RuntimePlugin + 'static) -> Self { + self.operation_plugins + .push(SharedRuntimePlugin::new(plugin)); self } - pub fn apply_client_configuration(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + pub fn apply_client_configuration( + &self, + cfg: &mut ConfigBag, + ) -> Result { + tracing::trace!("applying client runtime plugins"); + let mut builder = RuntimeComponentsBuilder::new("apply_client_configuration"); for plugin in self.client_plugins.iter() { - plugin.configure(cfg)?; + if let Some(layer) = plugin.config() { + cfg.push_shared_layer(layer); + } + builder = builder.merge_from(&plugin.runtime_components()); } - - Ok(()) + Ok(builder) } - pub fn apply_operation_configuration(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + pub fn apply_operation_configuration( + &self, + cfg: &mut ConfigBag, + ) -> Result { + tracing::trace!("applying operation runtime plugins"); + let mut builder = RuntimeComponentsBuilder::new("apply_operation_configuration"); for plugin in self.operation_plugins.iter() { - plugin.configure(cfg)?; + if let Some(layer) = plugin.config() { + cfg.push_shared_layer(layer); + } + builder = builder.merge_from(&plugin.runtime_components()); } - - Ok(()) + Ok(builder) } } #[cfg(test)] mod tests { - use super::{BoxError, RuntimePlugin, RuntimePlugins}; - use crate::config_bag::ConfigBag; + use super::{RuntimePlugin, RuntimePlugins}; + #[derive(Debug)] struct SomeStruct; - impl RuntimePlugin for SomeStruct { - fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { - todo!() - } - } + impl RuntimePlugin for SomeStruct {} #[test] fn can_add_runtime_plugin_implementors_to_runtime_plugins() { RuntimePlugins::new().with_client_plugin(SomeStruct); } + + #[test] + fn runtime_plugins_are_send_sync() { + fn assert_send_sync() {} + assert_send_sync::(); + } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/ser_de.rs b/rust-runtime/aws-smithy-runtime-api/src/client/ser_de.rs new file mode 100644 index 0000000000..5a3a77988e --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/ser_de.rs @@ -0,0 +1,105 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Serialization/deserialization for the orchestrator. + +use crate::box_error::BoxError; +use crate::client::interceptors::context::{Error, Input, Output}; +use crate::client::orchestrator::{HttpRequest, HttpResponse, OrchestratorError}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use std::fmt; +use std::sync::Arc; + +/// Serialization implementation that converts an [`Input`] into an [`HttpRequest`]. +pub trait RequestSerializer: Send + Sync + fmt::Debug { + /// Serializes the input into an HTTP request. + /// + /// The type of the [`Input`] must be known ahead of time by the request serializer + /// implementation, and must be downcasted to get access to the information necessary + /// for serialization. + /// + /// The request serializer is generally added to the [`ConfigBag`] by the operation's + /// code generated runtime plugin, which is aware of the correct input/output/error types. + fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result; +} + +/// A shared request serializer. +/// +/// This is a simple shared ownership wrapper type for the [`RequestSerializer`] trait. +#[derive(Clone, Debug)] +pub struct SharedRequestSerializer(Arc); + +impl SharedRequestSerializer { + /// Creates a new shared request serializer. + pub fn new(serializer: impl RequestSerializer + 'static) -> Self { + Self(Arc::new(serializer)) + } +} + +impl RequestSerializer for SharedRequestSerializer { + fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result { + self.0.serialize_input(input, cfg) + } +} + +impl Storable for SharedRequestSerializer { + type Storer = StoreReplace; +} + +/// Deserialization implementation that converts an [`HttpResponse`] into an [`Output`] or [`Error`]. +pub trait ResponseDeserializer: Send + Sync + fmt::Debug { + /// For streaming requests, deserializes the response headers. + /// + /// The orchestrator will call `deserialize_streaming` first, and if it returns `None`, + /// then it will continue onto `deserialize_nonstreaming`. This method should only be + /// implemented for streaming requests where the streaming response body needs to be a part + /// of the deserialized output. + fn deserialize_streaming( + &self, + response: &mut HttpResponse, + ) -> Option>> { + let _ = response; + None + } + + /// Deserialize the entire response including its body into an output or error. + fn deserialize_nonstreaming( + &self, + response: &HttpResponse, + ) -> Result>; +} + +/// Shared response deserializer. +/// +/// This is a simple shared ownership wrapper type for the [`ResponseDeserializer`] trait. +#[derive(Debug)] +pub struct SharedResponseDeserializer(Arc); + +impl SharedResponseDeserializer { + /// Creates a new [`SharedResponseDeserializer`]. + pub fn new(serializer: impl ResponseDeserializer + 'static) -> Self { + Self(Arc::new(serializer)) + } +} + +impl ResponseDeserializer for SharedResponseDeserializer { + fn deserialize_nonstreaming( + &self, + response: &HttpResponse, + ) -> Result> { + self.0.deserialize_nonstreaming(response) + } + + fn deserialize_streaming( + &self, + response: &mut HttpResponse, + ) -> Option>> { + self.0.deserialize_streaming(response) + } +} + +impl Storable for SharedResponseDeserializer { + type Storer = StoreReplace; +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs deleted file mode 100644 index 78e7b2502e..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Layered Configuration Bag Structure -//! -//! [`config_bag::ConfigBag`] and [`config_bag::FrozenConfigBag`] are the two representations of a layered configuration structure -//! with the following properties: -//! 1. A new layer of configuration may be applied onto an existing configuration structure without modifying it or taking ownership. -//! 2. No lifetime shenanigans to deal with -use aws_smithy_http::property_bag::PropertyBag; -use std::any::type_name; -use std::fmt::Debug; -use std::ops::Deref; -use std::sync::Arc; - -/// Layered Configuration Structure -/// -/// [`ConfigBag`] is the "unlocked" form of the bag. Only the top layer of the bag may be unlocked. -#[must_use] -pub struct ConfigBag { - head: Layer, - tail: Option, -} - -/// Layered Configuration Structure -/// -/// [`FrozenConfigBag`] is the "locked" form of the bag. -#[derive(Clone)] -#[must_use] -pub struct FrozenConfigBag(Arc); - -impl Deref for FrozenConfigBag { - type Target = ConfigBag; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -pub trait Persist { - fn layer_name(&self) -> &'static str; - fn persist(&self, layer: &mut ConfigBag); -} - -pub trait Load: Sized { - fn load(bag: &ConfigBag) -> Option; -} - -pub trait ConfigLayer: Persist + Load {} - -enum Value { - Set(T), - ExplicitlyUnset, -} - -struct Layer { - name: &'static str, - props: PropertyBag, -} - -fn no_op(_: &mut ConfigBag) {} - -impl FrozenConfigBag { - /// Attempts to convert this bag directly into a [`ConfigBag`] if no other references exist - /// - /// This allows modifying the top layer of the bag. [`Self::add_layer`] may be - /// used to add a new layer to the bag. - pub fn try_modify(self) -> Option { - Arc::try_unwrap(self.0).ok() - } - - /// Add a new layer to the config bag - /// - /// This is equivalent to calling [`Self::with_fn`] with a no-op function - /// - /// # Examples - /// ``` - /// use aws_smithy_runtime_api::config_bag::ConfigBag; - /// fn add_more_config(bag: &mut ConfigBag) { /* ... */ } - /// let bag = ConfigBag::base().with_fn("first layer", |_| { /* add a property */ }); - /// let mut bag = bag.add_layer("second layer"); - /// add_more_config(&mut bag); - /// let bag = bag.freeze(); - /// ``` - pub fn add_layer(&self, name: &'static str) -> ConfigBag { - self.with_fn(name, no_op) - } - - pub fn with(&self, layer: impl Persist) -> ConfigBag { - self.with_fn(layer.layer_name(), |bag| layer.persist(bag)) - } - - /// Add more items to the config bag - pub fn with_fn(&self, name: &'static str, next: impl Fn(&mut ConfigBag)) -> ConfigBag { - let new_layer = Layer { - name, - props: PropertyBag::new(), - }; - let mut bag = ConfigBag { - head: new_layer, - tail: Some(self.clone()), - }; - next(&mut bag); - bag - } -} - -impl ConfigBag { - pub fn base() -> Self { - ConfigBag { - head: Layer { - name: "base", - props: Default::default(), - }, - tail: None, - } - } - - /// Retrieve the value of type `T` from the bag if exists - pub fn get(&self) -> Option<&T> { - let mut source = vec![]; - let out = self.sourced_get(&mut source); - println!("searching for {:?} {:#?}", type_name::(), source); - out - } - - /// Insert `value` into the bag - pub fn put(&mut self, value: T) -> &mut Self { - self.head.props.insert(Value::Set(value)); - self - } - - /// Remove `T` from this bag - pub fn unset(&mut self) -> &mut Self { - self.head.props.insert(Value::::ExplicitlyUnset); - self - } - - /// Freeze this layer by wrapping it in an `Arc` - /// - /// This prevents further items from being added to this layer, but additional layers can be - /// added to the bag. - pub fn freeze(self) -> FrozenConfigBag { - self.into() - } - - /// Add another layer to this configuration bag - /// - /// Hint: If you want to re-use this layer, call `freeze` first. - /// ``` - /// use aws_smithy_runtime_api::config_bag::ConfigBag; - /// let bag = ConfigBag::base(); - /// let first_layer = bag.with_fn("a", |b: &mut ConfigBag| { b.put("a"); }).freeze(); - /// let second_layer = first_layer.with_fn("other", |b: &mut ConfigBag| { b.put(1i32); }); - /// // The number is only in the second layer - /// assert_eq!(first_layer.get::(), None); - /// assert_eq!(second_layer.get::(), Some(&1)); - /// - /// // The string is in both layers - /// assert_eq!(first_layer.get::<&'static str>(), Some(&"a")); - /// assert_eq!(second_layer.get::<&'static str>(), Some(&"a")); - /// ``` - pub fn with_fn(self, name: &'static str, next: impl Fn(&mut ConfigBag)) -> ConfigBag { - self.freeze().with_fn(name, next) - } - - pub fn with(self, layer: impl Persist) -> ConfigBag { - self.freeze().with(layer) - } - - pub fn add_layer(self, name: &'static str) -> ConfigBag { - self.freeze().add_layer(name) - } - - pub fn sourced_get( - &self, - source_trail: &mut Vec, - ) -> Option<&T> { - // todo: optimize so we don't need to compute the source if it's unused - let bag = &self.head; - let inner_item = self - .tail - .as_ref() - .and_then(|bag| bag.sourced_get(source_trail)); - let (item, source) = match bag.props.get::>() { - Some(Value::ExplicitlyUnset) => (None, SourceInfo::Unset { layer: bag.name }), - Some(Value::Set(v)) => ( - Some(v), - SourceInfo::Set { - layer: bag.name, - value: format!("{:?}", v), - }, - ), - None => (inner_item, SourceInfo::Inherit { layer: bag.name }), - }; - source_trail.push(source); - item - } -} - -impl From for FrozenConfigBag { - fn from(bag: ConfigBag) -> Self { - FrozenConfigBag(Arc::new(bag)) - } -} - -#[derive(Debug)] -pub enum SourceInfo { - Set { layer: &'static str, value: String }, - Unset { layer: &'static str }, - Inherit { layer: &'static str }, -} - -#[cfg(test)] -mod test { - use super::ConfigBag; - use crate::config_bag::{Load, Persist}; - - #[test] - fn layered_property_bag() { - #[derive(Debug)] - struct Prop1; - #[derive(Debug)] - struct Prop2; - let layer_a = |bag: &mut ConfigBag| { - bag.put(Prop1); - }; - - let layer_b = |bag: &mut ConfigBag| { - bag.put(Prop2); - }; - - #[derive(Debug)] - struct Prop3; - - let mut base_bag = ConfigBag::base() - .with_fn("a", layer_a) - .with_fn("b", layer_b); - base_bag.put(Prop3); - assert!(base_bag.get::().is_some()); - - #[derive(Debug)] - struct Prop4; - - let layer_c = |bag: &mut ConfigBag| { - bag.put(Prop4); - bag.unset::(); - }; - - let base_bag = base_bag.freeze(); - let final_bag = base_bag.with_fn("c", layer_c); - - assert!(final_bag.get::().is_some()); - assert!(base_bag.get::().is_none()); - assert!(final_bag.get::().is_some()); - assert!(final_bag.get::().is_some()); - // we unset prop3 - assert!(final_bag.get::().is_none()); - } - - #[test] - fn config_bag() { - let bag = ConfigBag::base(); - #[derive(Debug)] - struct Region(&'static str); - let bag = bag.with_fn("service config", |layer: &mut ConfigBag| { - layer.put(Region("asdf")); - }); - - assert_eq!(bag.get::().unwrap().0, "asdf"); - - #[derive(Debug)] - struct SigningName(&'static str); - let bag = bag.freeze(); - let operation_config = bag.with_fn("operation", |layer: &mut ConfigBag| { - layer.put(SigningName("s3")); - }); - - assert!(bag.get::().is_none()); - assert_eq!(operation_config.get::().unwrap().0, "s3"); - - let mut open_bag = operation_config.with_fn("my_custom_info", |_bag: &mut ConfigBag| {}); - open_bag.put("foo"); - } - - #[test] - fn persist_trait() { - #[derive(Debug, Eq, PartialEq, Clone)] - struct MyConfig { - a: bool, - b: String, - } - - #[derive(Debug)] - struct A(bool); - #[derive(Debug)] - struct B(String); - - impl Persist for MyConfig { - fn layer_name(&self) -> &'static str { - "my_config" - } - - fn persist(&self, layer: &mut ConfigBag) { - layer.put(A(self.a)); - layer.put(B(self.b.clone())); - } - } - impl Load for MyConfig { - fn load(bag: &ConfigBag) -> Option { - Some(MyConfig { - a: bag.get::().unwrap().0, - b: bag.get::().unwrap().0.clone(), - }) - } - } - - let conf = MyConfig { - a: true, - b: "hello!".to_string(), - }; - - let bag = ConfigBag::base().with(conf.clone()); - - assert_eq!(MyConfig::load(&bag), Some(conf)); - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/lib.rs b/rust-runtime/aws-smithy-runtime-api/src/lib.rs index 94e0efc9f3..ebc9225a8a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/lib.rs @@ -4,20 +4,33 @@ */ #![warn( - // missing_docs, + missing_docs, rustdoc::missing_crate_level_docs, unreachable_pub, rust_2018_idioms )] #![allow(clippy::new_without_default)] -//! Basic types for the new smithy client orchestrator. +//! APIs needed to configure and customize the Smithy generated code. +//! +//! Most users will not need to use this crate directly as the most frequently used +//! APIs are re-exported in the generated clients. However, this crate will be useful +//! for anyone writing a library for others to use with their generated clients. +//! +//! If you're needing to depend on this and you're not writing a library for Smithy +//! generated clients, then please file an issue on [smithy-rs](https://github.com/awslabs/smithy-rs) +//! as we likely missed re-exporting one of the APIs. +//! +//! All client-specific code is in the [`client`](crate::client) root level module +//! to leave room for smithy-rs server APIs in the future. -/// Smithy runtime for client orchestration. -pub mod client; +/// A boxed error that is `Send` and `Sync`. +pub mod box_error; -/// A typemap for storing configuration. -pub mod config_bag; +/// APIs for client orchestration. +#[cfg(feature = "client")] +pub mod client; -/// Utilities for type erasure. -pub mod type_erasure; +/// Internal builder macros. Not intended to be used outside of the aws-smithy-runtime crates. +#[doc(hidden)] +pub mod macros; diff --git a/rust-runtime/aws-smithy-runtime-api/src/macros.rs b/rust-runtime/aws-smithy-runtime-api/src/macros.rs new file mode 100644 index 0000000000..759d1b8452 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/macros.rs @@ -0,0 +1,168 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Various utility macros to aid runtime crate writers. + +/// Define a new builder struct, along with a method to create it, and setters. +/// +/// ## Examples +/// +/// The builder macro takes a list of field definitions, each with four elements: +/// ```txt +/// set_optional_field, optional_field, String, "An optional field which may or may not be set when `.build()` is called.", +/// ^ The setter name, ^ The field name, ^ The type, ^ The documentation for the field and setter methods. +/// ``` +/// +/// The following example creates a new builder struct, along with a method to create it, and setters +/// for a struct `MyConfig` with three fields: +/// +/// ``` +/// use std::collections::HashMap; +/// use std::sync::{Arc, Mutex}; +/// use aws_smithy_runtime_api::{builder, builder_methods, builder_struct}; +/// +/// struct MyConfig { +/// optional_field: Option, +/// optional_field_with_a_default: f64, +/// required_field_with_no_default: Arc>>, +/// } +/// +/// impl MyConfig { +/// pub fn builder() -> Builder { +/// Builder::new() +/// } +/// } +/// +/// builder!( +/// set_optional_field, optional_field, String, "An optional field which may or may not be set when `.build()` is called.", +/// set_optional_field_with_a_default, optional_field_with_a_default, f64, "An optional field that will default to `f64::MAX` if it's unset when `.build()` is called.", +/// set_required_field_with_no_default, required_field_with_no_default, HashMap, "A required field that will cause the builder to panic if it's unset when `.build()` is called." +/// ); +/// +/// impl Builder { +/// fn build(self) -> MyConfig { +/// MyConfig { +/// optional_field: self.optional_field, +/// optional_field_with_a_default: self.optional_field_with_a_default.unwrap_or(f64::MAX), +/// required_field_with_no_default: Arc::new(Mutex::new( +/// self.required_field_with_no_default.expect("'required_field_with_no_default' is required") +/// )), +/// } +/// } +/// } +/// ``` +/// +/// In this example, the result of macro expansion would look like this: +/// +/// ``` +/// # use std::collections::HashMap; +/// # use std::sync::{Arc, Mutex}; +/// #[derive(Clone, Debug, Default)] +/// pub struct Builder { +/// #[doc = "An optional field which may or may not be set when `.build()` is called."] +/// optional_field: Option, +/// #[doc = "An optional field that will default to `f64::MAX` if it's unset when `.build()` is called."] +/// optional_field_with_a_default: Option, +/// #[doc = "A required field that will cause the builder to panic if it's unset when `.build()` is called."] +/// required_field_with_no_default: Option>, +/// } +/// +/// impl Builder { +/// pub fn new() -> Self { +/// Builder::default() +/// } +/// +/// #[doc = "An optional field which may or may not be set when `.build()` is called."] +/// pub fn set_optional_field(&mut self, optional_field: Option) -> &mut Self { +/// self.optional_field = optional_field; +/// self +/// } +/// +/// #[doc = "An optional field which may or may not be set when `.build()` is called."] +/// pub fn optional_field(mut self, optional_field: String) -> Self { +/// self.optional_field = Some(optional_field); +/// self +/// } +/// +/// #[doc = "An optional field that will default to `f64::MAX` if it's unset when `.build()` is called."] +/// pub fn set_optional_field_with_a_default(&mut self, optional_field_with_a_default: Option) -> &mut Self { +/// self.optional_field_with_a_default = optional_field_with_a_default; +/// self +/// } +/// +/// #[doc = "An optional field that will default to `f64::MAX` if it's unset when `.build()` is called."] +/// pub fn optional_field_with_a_default(mut self, optional_field_with_a_default: f64) -> Self { +/// self.optional_field_with_a_default = Some(optional_field_with_a_default); +/// self +/// } +/// +/// #[doc = "A required field that will cause the builder to panic if it's unset when `.build()` is called."] +/// pub fn set_required_field_with_no_default(&mut self, required_field_with_no_default: Option>) -> &mut Self { +/// self.required_field_with_no_default = required_field_with_no_default; +/// self +/// } +/// +/// #[doc = "A required field that will cause the builder to panic if it's unset when `.build()` is called."] +/// pub fn required_field_with_no_default(mut self, required_field_with_no_default: HashMap) -> Self { +/// self.required_field_with_no_default = Some(required_field_with_no_default); +/// self +/// } +/// } +/// ``` +#[doc(hidden)] +#[macro_export] +macro_rules! builder { + ($($tt:tt)+) => { + builder_struct!($($tt)+); + + impl Builder { + pub fn new() -> Self { + Builder::default() + } + + builder_methods!($($tt)+); + } + } +} + +/// Define a new builder struct, its fields, and their docs. This macro is intended to be called +/// by the `builder!` macro and should not be called directly. +#[doc(hidden)] +#[macro_export] +macro_rules! builder_struct { + ($($_setter_name:ident, $field_name:ident, $ty:ty, $doc:literal $(,)?)+) => { + #[derive(Clone, Debug, Default)] + pub struct Builder { + $( + #[doc = $doc] + $field_name: Option<$ty>, + )+ + } + } +} + +/// Define setter methods for a builder struct. Must be called from within an `impl` block. This +/// macro is intended to be called by the `builder!` macro and should not be called directly. +#[doc(hidden)] +#[macro_export] +macro_rules! builder_methods { + ($fn_name:ident, $arg_name:ident, $ty:ty, $doc:literal, $($tail:tt)+) => { + builder_methods!($fn_name, $arg_name, $ty, $doc); + builder_methods!($($tail)+); + }; + ($fn_name:ident, $arg_name:ident, $ty:ty, $doc:literal) => { + #[doc = $doc] + pub fn $fn_name(&mut self, $arg_name: Option<$ty>) -> &mut Self { + self.$arg_name = $arg_name; + self + } + + #[doc = $doc] + pub fn $arg_name(mut self, $arg_name: $ty) -> Self { + self.$arg_name = Some($arg_name); + self + } + }; +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs deleted file mode 100644 index 50c852f0cf..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use std::any::Any; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; - -/// A [`TypeErasedBox`] with type information tracked via generics at compile-time -/// -/// `TypedBox` is used to transition to/from a `TypeErasedBox`. A `TypedBox` can only -/// be created from a `T` or from a `TypeErasedBox` value that _is a_ `T`. Therefore, it can -/// be assumed to be a `T` even though the underlying storage is still a `TypeErasedBox`. -/// Since the `T` is only used in `PhantomData`, it gets compiled down to just a `TypeErasedBox`. -/// -/// The orchestrator uses `TypeErasedBox` to avoid the complication of six or more generic parameters -/// and to avoid the monomorphization that brings with it. This `TypedBox` will primarily be useful -/// for operation-specific or service-specific interceptors that need to operate on the actual -/// input/output/error types. -#[derive(Debug)] -pub struct TypedBox { - inner: TypeErasedBox, - _phantom: PhantomData, -} - -impl TypedBox -where - T: Send + Sync + 'static, -{ - // Creates a new `TypedBox`. - pub fn new(inner: T) -> Self { - Self { - inner: TypeErasedBox::new(Box::new(inner) as _), - _phantom: Default::default(), - } - } - - // Tries to create a `TypedBox` from a `TypeErasedBox`. - // - // If the `TypedBox` can't be created due to the `TypeErasedBox`'s value consisting - // of another type, then the original `TypeErasedBox` will be returned in the `Err` variant. - pub fn assume_from(type_erased: TypeErasedBox) -> Result, TypeErasedBox> { - if type_erased.downcast_ref::().is_some() { - Ok(TypedBox { - inner: type_erased, - _phantom: Default::default(), - }) - } else { - Err(type_erased) - } - } - - /// Converts the `TypedBox` back into `T`. - pub fn unwrap(self) -> T { - *self.inner.downcast::().expect("type checked") - } - - /// Converts the `TypedBox` into a `TypeErasedBox`. - pub fn erase(self) -> TypeErasedBox { - self.inner - } -} - -impl Deref for TypedBox { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.downcast_ref().expect("type checked") - } -} - -impl DerefMut for TypedBox { - fn deref_mut(&mut self) -> &mut Self::Target { - self.inner.downcast_mut().expect("type checked") - } -} - -#[derive(Debug)] -pub struct TypedRef<'a, T> { - inner: &'a TypeErasedBox, - _phantom: PhantomData, -} - -impl<'a, T: 'static> TypedRef<'a, T> { - pub fn assume_from(type_erased: &'a TypeErasedBox) -> Option> { - if type_erased.downcast_ref::().is_some() { - Some(TypedRef { - inner: type_erased, - _phantom: Default::default(), - }) - } else { - None - } - } -} - -impl<'a, T: 'static> Deref for TypedRef<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.downcast_ref().expect("type checked") - } -} - -/// A new-type around `Box` -#[derive(Debug)] -pub struct TypeErasedBox { - inner: Box, -} - -impl TypeErasedBox { - // Creates a new `TypeErasedBox`. - pub fn new(inner: Box) -> Self { - Self { inner } - } - - // Downcast into a `Box`, or return `Self` if it is not a `T`. - pub fn downcast(self) -> Result, Self> { - match self.inner.downcast() { - Ok(t) => Ok(t), - Err(s) => Err(Self { inner: s }), - } - } - - /// Downcast as a `&T`, or return `None` if it is not a `T`. - pub fn downcast_ref(&self) -> Option<&T> { - self.inner.downcast_ref() - } - - /// Downcast as a `&mut T`, or return `None` if it is not a `T`. - pub fn downcast_mut(&mut self) -> Option<&mut T> { - self.inner.downcast_mut() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[derive(Debug)] - struct Foo(&'static str); - #[derive(Debug)] - struct Bar(isize); - - #[test] - fn test() { - let foo = TypedBox::new(Foo("1")); - let bar = TypedBox::new(Bar(2)); - - let mut foo_erased = foo.erase(); - foo_erased - .downcast_mut::() - .expect("I know its a Foo") - .0 = "3"; - - let bar_erased = bar.erase(); - - let bar_erased = TypedBox::::assume_from(bar_erased).expect_err("it's not a Foo"); - let mut bar = TypedBox::::assume_from(bar_erased).expect("it's a Bar"); - assert_eq!(2, bar.0); - bar.0 += 1; - - let bar = bar.unwrap(); - assert_eq!(3, bar.0); - - assert!(foo_erased.downcast_ref::().is_none()); - assert!(foo_erased.downcast_mut::().is_none()); - let mut foo_erased = foo_erased.downcast::().expect_err("it's not a Bar"); - - assert_eq!("3", foo_erased.downcast_ref::().expect("it's a Foo").0); - foo_erased.downcast_mut::().expect("it's a Foo").0 = "4"; - let foo = *foo_erased.downcast::().expect("it's a Foo"); - assert_eq!("4", foo.0); - } -} diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 5227691cbb..e761544d3d 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -6,24 +6,40 @@ description = "The new smithy runtime crate" edition = "2021" license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" -publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -test-util = ["dep:aws-smithy-protocol-test"] +client = ["aws-smithy-runtime-api/client"] +http-auth = ["aws-smithy-runtime-api/http-auth"] +test-util = ["aws-smithy-runtime-api/test-util", "dep:aws-smithy-protocol-test", "dep:tracing-subscriber"] [dependencies] +aws-smithy-async = { path = "../aws-smithy-async" } +aws-smithy-client = { path = "../aws-smithy-client" } aws-smithy-http = { path = "../aws-smithy-http" } aws-smithy-protocol-test = { path = "../aws-smithy-protocol-test", optional = true } aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" } aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" +fastrand = "2.0.0" http = "0.2.8" http-body = "0.4.5" +once_cell = "1.18.0" +pin-project-lite = "0.2.7" pin-utils = "0.1.0" tokio = { version = "1.25", features = [] } -tracing = "0.1" +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.16", optional = true, features = ["fmt", "json"] } + +[dev-dependencies] +approx = "0.5.1" +aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio", "test-util"] } +aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["test-util"] } +aws-smithy-types = { path = "../aws-smithy-types", features = ["test-util"] } +tokio = { version = "1.25", features = ["macros", "rt", "test-util"] } +tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } +tracing-test = "0.2.1" [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-runtime/README.md b/rust-runtime/aws-smithy-runtime/README.md index ba6dbc29ae..667b2267ff 100644 --- a/rust-runtime/aws-smithy-runtime/README.md +++ b/rust-runtime/aws-smithy-runtime/README.md @@ -1,8 +1,6 @@ -# aws-smithy-orchestrator +# aws-smithy-runtime -**This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.** - -Code which enables users to access a smithy service. Handles the configuration and construction of requests, as well as responses. +Runtime support logic and types for smithy-rs generated code. This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. diff --git a/rust-runtime/aws-smithy-runtime/additional-ci b/rust-runtime/aws-smithy-runtime/additional-ci new file mode 100755 index 0000000000..b44c6c05be --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/additional-ci @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# This script contains additional CI checks to run for this specific package + +set -e + +echo "### Testing every combination of features (excluding --all-features)" +cargo hack test --feature-powerset --exclude-all-features diff --git a/rust-runtime/aws-smithy-runtime/external-types.toml b/rust-runtime/aws-smithy-runtime/external-types.toml index 45467fc294..a639820020 100644 --- a/rust-runtime/aws-smithy-runtime/external-types.toml +++ b/rust-runtime/aws-smithy-runtime/external-types.toml @@ -1,9 +1,12 @@ allowed_external_types = [ "aws_smithy_runtime_api::*", + "aws_smithy_async::*", "aws_smithy_http::*", + "aws_smithy_types::*", + "aws_smithy_client::erase::DynConnector", # TODO(audit-external-type-usage) We should newtype these or otherwise avoid exposing them "http::header::name::HeaderName", "http::request::Request", "http::response::Response", - "uri::Uri", + "http::uri::Uri", ] diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index e41bda8ed2..629b558c32 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -3,7 +3,40 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Smithy auth scheme implementations. +pub mod auth; + +/// Smithy code related to connectors and connections. +/// +/// A "connector" manages one or more "connections", handles connection timeouts, re-establishes +/// connections, etc. +/// +/// "Connections" refers to the actual transport layer implementation of the connector. +/// By default, the orchestrator uses a connector provided by `hyper`. +pub mod connectors; + +/// Utility to simplify config building for config and config overrides. +pub mod config_override; + +/// The client orchestrator implementation pub mod orchestrator; -/// Smithy connector runtime plugins -pub mod connections; +/// Smithy code related to retry handling and token buckets. +/// +/// This code defines when and how failed requests should be retried. It also defines the behavior +/// used to limit the rate at which requests are sent. +pub mod retries; + +/// Utilities for testing orchestrators. An orchestrator missing required components will panic when +/// run. This module contains stub components that can be used when you only care about testing some +/// specific aspect of the orchestrator. +#[cfg(feature = "test-util")] +pub mod test_util; + +mod timeout; + +/// Smithy identity used by auth and signing. +pub mod identity; + +/// Interceptors for Smithy clients. +pub mod interceptors; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth.rs new file mode 100644 index 0000000000..a67537cb6f --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/auth.rs @@ -0,0 +1,9 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub mod no_auth; + +#[cfg(feature = "http-auth")] +pub mod http; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs new file mode 100644 index 0000000000..1e0efcd1c4 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -0,0 +1,392 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Auth scheme implementations for HTTP API Key, Basic Auth, Bearer Token, and Digest auth. + +use aws_smithy_http::query_writer::QueryWriter; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::auth::http::{ + HTTP_API_KEY_AUTH_SCHEME_ID, HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, + HTTP_DIGEST_AUTH_SCHEME_ID, +}; +use aws_smithy_runtime_api::client::auth::{ + AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, Signer, +}; +use aws_smithy_runtime_api::client::identity::http::{Login, Token}; +use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_components::{GetIdentityResolver, RuntimeComponents}; +use aws_smithy_types::base64::encode; +use aws_smithy_types::config_bag::ConfigBag; +use http::header::HeaderName; +use http::HeaderValue; + +/// Destination for the API key +#[derive(Copy, Clone, Debug)] +pub enum ApiKeyLocation { + /// Place the API key in the URL query parameters + Query, + /// Place the API key in the request headers + Header, +} + +/// Auth implementation for Smithy's `@httpApiKey` auth scheme +#[derive(Debug)] +pub struct ApiKeyAuthScheme { + signer: ApiKeySigner, +} + +impl ApiKeyAuthScheme { + /// Creates a new `ApiKeyAuthScheme`. + pub fn new( + scheme: impl Into, + location: ApiKeyLocation, + name: impl Into, + ) -> Self { + Self { + signer: ApiKeySigner { + scheme: scheme.into(), + location, + name: name.into(), + }, + } + } +} + +impl AuthScheme for ApiKeyAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + HTTP_API_KEY_AUTH_SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn signer(&self) -> &dyn Signer { + &self.signer + } +} + +#[derive(Debug)] +struct ApiKeySigner { + scheme: String, + location: ApiKeyLocation, + name: String, +} + +impl Signer for ApiKeySigner { + fn sign_http_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + let api_key = identity + .data::() + .ok_or("HTTP ApiKey auth requires a `Token` identity")?; + match self.location { + ApiKeyLocation::Header => { + request.headers_mut().append( + HeaderName::try_from(&self.name).expect("valid API key header name"), + HeaderValue::try_from(format!("{} {}", self.scheme, api_key.token())).map_err( + |_| "API key contains characters that can't be included in a HTTP header", + )?, + ); + } + ApiKeyLocation::Query => { + let mut query = QueryWriter::new(request.uri()); + query.insert(&self.name, api_key.token()); + *request.uri_mut() = query.build_uri(); + } + } + + Ok(()) + } +} + +/// Auth implementation for Smithy's `@httpBasicAuth` auth scheme +#[derive(Debug, Default)] +pub struct BasicAuthScheme { + signer: BasicAuthSigner, +} + +impl BasicAuthScheme { + /// Creates a new `BasicAuthScheme`. + pub fn new() -> Self { + Self { + signer: BasicAuthSigner, + } + } +} + +impl AuthScheme for BasicAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + HTTP_BASIC_AUTH_SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn signer(&self) -> &dyn Signer { + &self.signer + } +} + +#[derive(Debug, Default)] +struct BasicAuthSigner; + +impl Signer for BasicAuthSigner { + fn sign_http_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + let login = identity + .data::() + .ok_or("HTTP basic auth requires a `Login` identity")?; + request.headers_mut().insert( + http::header::AUTHORIZATION, + HeaderValue::from_str(&format!( + "Basic {}", + encode(format!("{}:{}", login.user(), login.password())) + )) + .expect("valid header value"), + ); + Ok(()) + } +} + +/// Auth implementation for Smithy's `@httpBearerAuth` auth scheme +#[derive(Debug, Default)] +pub struct BearerAuthScheme { + signer: BearerAuthSigner, +} + +impl BearerAuthScheme { + /// Creates a new `BearerAuthScheme`. + pub fn new() -> Self { + Self { + signer: BearerAuthSigner, + } + } +} + +impl AuthScheme for BearerAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + HTTP_BEARER_AUTH_SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn signer(&self) -> &dyn Signer { + &self.signer + } +} + +#[derive(Debug, Default)] +struct BearerAuthSigner; + +impl Signer for BearerAuthSigner { + fn sign_http_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + let token = identity + .data::() + .ok_or("HTTP bearer auth requires a `Token` identity")?; + request.headers_mut().insert( + http::header::AUTHORIZATION, + HeaderValue::from_str(&format!("Bearer {}", token.token())).map_err(|_| { + "Bearer token contains characters that can't be included in a HTTP header" + })?, + ); + Ok(()) + } +} + +/// Auth implementation for Smithy's `@httpDigestAuth` auth scheme +#[derive(Debug, Default)] +pub struct DigestAuthScheme { + signer: DigestAuthSigner, +} + +impl DigestAuthScheme { + /// Creates a new `DigestAuthScheme`. + pub fn new() -> Self { + Self { + signer: DigestAuthSigner, + } + } +} + +impl AuthScheme for DigestAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + HTTP_DIGEST_AUTH_SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn signer(&self) -> &dyn Signer { + &self.signer + } +} + +#[derive(Debug, Default)] +struct DigestAuthSigner; + +impl Signer for DigestAuthSigner { + fn sign_http_request( + &self, + _request: &mut HttpRequest, + _identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + unimplemented!( + "support for signing with Smithy's `@httpDigestAuth` auth scheme is not implemented yet" + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::identity::http::Login; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + + #[test] + fn test_api_key_signing_headers() { + let signer = ApiKeySigner { + scheme: "SomeSchemeName".into(), + location: ApiKeyLocation::Header, + name: "some-header-name".into(), + }; + let runtime_components = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let config_bag = ConfigBag::base(); + let identity = Identity::new(Token::new("some-token", None), None); + let mut request = http::Request::builder() + .uri("http://example.com/Foobaz") + .body(SdkBody::empty()) + .unwrap(); + signer + .sign_http_request( + &mut request, + &identity, + AuthSchemeEndpointConfig::empty(), + &runtime_components, + &config_bag, + ) + .expect("success"); + assert_eq!( + "SomeSchemeName some-token", + request.headers().get("some-header-name").unwrap() + ); + assert_eq!("http://example.com/Foobaz", request.uri().to_string()); + } + + #[test] + fn test_api_key_signing_query() { + let signer = ApiKeySigner { + scheme: "".into(), + location: ApiKeyLocation::Query, + name: "some-query-name".into(), + }; + let runtime_components = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let config_bag = ConfigBag::base(); + let identity = Identity::new(Token::new("some-token", None), None); + let mut request = http::Request::builder() + .uri("http://example.com/Foobaz") + .body(SdkBody::empty()) + .unwrap(); + signer + .sign_http_request( + &mut request, + &identity, + AuthSchemeEndpointConfig::empty(), + &runtime_components, + &config_bag, + ) + .expect("success"); + assert!(request.headers().get("some-query-name").is_none()); + assert_eq!( + "http://example.com/Foobaz?some-query-name=some-token", + request.uri().to_string() + ); + } + + #[test] + fn test_basic_auth() { + let signer = BasicAuthSigner; + let runtime_components = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let config_bag = ConfigBag::base(); + let identity = Identity::new(Login::new("Aladdin", "open sesame", None), None); + let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); + + signer + .sign_http_request( + &mut request, + &identity, + AuthSchemeEndpointConfig::empty(), + &runtime_components, + &config_bag, + ) + .expect("success"); + assert_eq!( + "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", + request.headers().get("Authorization").unwrap() + ); + } + + #[test] + fn test_bearer_auth() { + let signer = BearerAuthSigner; + + let config_bag = ConfigBag::base(); + let runtime_components = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let identity = Identity::new(Token::new("some-token", None), None); + let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); + signer + .sign_http_request( + &mut request, + &identity, + AuthSchemeEndpointConfig::empty(), + &runtime_components, + &config_bag, + ) + .expect("success"); + assert_eq!( + "Bearer some-token", + request.headers().get("Authorization").unwrap() + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs new file mode 100644 index 0000000000..ebda0f6943 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs @@ -0,0 +1,108 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! The [`NoAuthRuntimePlugin`] and supporting code. + +use crate::client::identity::no_auth::NoAuthIdentityResolver; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::auth::{ + AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, SharedAuthScheme, Signer, +}; +use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_components::{ + GetIdentityResolver, RuntimeComponents, RuntimeComponentsBuilder, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::ConfigBag; +use std::borrow::Cow; + +/// Auth scheme ID for "no auth". +pub const NO_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("no_auth"); + +/// A [`RuntimePlugin`] that registers a "no auth" identity resolver and auth scheme. +/// +/// This plugin can be used to disable authentication in certain cases, such as when there is +/// a Smithy `@optionalAuth` trait. +#[non_exhaustive] +#[derive(Debug)] +pub struct NoAuthRuntimePlugin(RuntimeComponentsBuilder); + +impl Default for NoAuthRuntimePlugin { + fn default() -> Self { + Self::new() + } +} + +impl NoAuthRuntimePlugin { + /// Creates a new `NoAuthRuntimePlugin`. + pub fn new() -> Self { + Self( + RuntimeComponentsBuilder::new("NoAuthRuntimePlugin") + .with_identity_resolver( + NO_AUTH_SCHEME_ID, + SharedIdentityResolver::new(NoAuthIdentityResolver::new()), + ) + .with_auth_scheme(SharedAuthScheme::new(NoAuthScheme::new())), + ) + } +} + +impl RuntimePlugin for NoAuthRuntimePlugin { + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.0) + } +} + +/// The "no auth" auth scheme. +/// +/// The orchestrator requires an auth scheme, so Smithy's `@optionalAuth` trait is implemented +/// by placing a "no auth" auth scheme at the end of the auth scheme options list so that it is +/// used if there's no identity resolver available for the other auth schemes. It's also used +/// for models that don't have auth at all. +#[derive(Debug, Default)] +pub struct NoAuthScheme { + signer: NoAuthSigner, +} + +impl NoAuthScheme { + /// Creates a new `NoAuthScheme`. + pub fn new() -> Self { + Self::default() + } +} + +#[derive(Debug, Default)] +struct NoAuthSigner; + +impl Signer for NoAuthSigner { + fn sign_http_request( + &self, + _request: &mut HttpRequest, + _identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + Ok(()) + } +} + +impl AuthScheme for NoAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + NO_AUTH_SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option { + identity_resolvers.identity_resolver(NO_AUTH_SCHEME_ID) + } + + fn signer(&self) -> &dyn Signer { + &self.signer + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/config_override.rs b/rust-runtime/aws-smithy-runtime/src/client/config_override.rs new file mode 100644 index 0000000000..07886a9526 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/config_override.rs @@ -0,0 +1,266 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; +use aws_smithy_types::config_bag::{ + CloneableLayer, FrozenLayer, Layer, Storable, Store, StoreReplace, +}; + +macro_rules! component { + ($typ:ty, $accessor:ident, $latest_accessor:ident, $doc:tt) => { + #[doc = $doc] + pub fn $accessor(&self) -> Option<$typ> { + fallback_component!(self, $typ, $accessor) + } + + #[doc = $doc] + pub fn $latest_accessor(&self) -> Option<$typ> { + latest_component!(self, $typ, $accessor) + } + }; +} +macro_rules! fallback_component { + ($self:ident, $typ:ty, $accessor:ident) => { + match &$self.inner { + Inner::Initial(initial) => initial.components.$accessor(), + Inner::Override(overrid) => overrid + .components + .$accessor() + .or_else(|| overrid.initial_components.$accessor()), + } + }; +} +macro_rules! latest_component { + ($self:ident, $typ:ty, $accessor:ident) => { + match &$self.inner { + Inner::Initial(initial) => initial.components.$accessor(), + Inner::Override(overrid) => overrid.components.$accessor(), + } + }; +} + +struct Initial<'a> { + config: &'a mut CloneableLayer, + components: &'a mut RuntimeComponentsBuilder, +} + +struct Override<'a> { + initial_config: FrozenLayer, + initial_components: &'a RuntimeComponentsBuilder, + config: &'a mut CloneableLayer, + components: &'a mut RuntimeComponentsBuilder, +} + +enum Inner<'a> { + Initial(Initial<'a>), + Override(Override<'a>), +} + +/// Utility to simplify config building and config overrides. +/// +/// The resolver allows the same initialization logic to be reused +/// for both initial config and override config. +/// +/// This resolver can be initialized to one of two modes: +/// 1. _Initial mode_: The resolver is being used in a service `Config` builder's `build()` method, and thus, +/// there is no config override at this point. +/// 2. _Override mode_: The resolver is being used by the `ConfigOverrideRuntimePlugin`'s constructor and needs +/// to incorporate both the original config and the given config override for this operation. +/// +/// In all the methods on [`Resolver`], the term "latest" refers to the initial config when in _Initial mode_, +/// and to config override when in _Override mode_. +pub struct Resolver<'a> { + inner: Inner<'a>, +} + +impl<'a> Resolver<'a> { + /// Construct a new [`Resolver`] in _initial mode_. + pub fn initial( + config: &'a mut CloneableLayer, + components: &'a mut RuntimeComponentsBuilder, + ) -> Self { + Self { + inner: Inner::Initial(Initial { config, components }), + } + } + + /// Construct a new [`Resolver`] in _override mode_. + pub fn overrid( + initial_config: FrozenLayer, + initial_components: &'a RuntimeComponentsBuilder, + config: &'a mut CloneableLayer, + components: &'a mut RuntimeComponentsBuilder, + ) -> Self { + Self { + inner: Inner::Override(Override { + initial_config, + initial_components, + config, + components, + }), + } + } + + /// Returns true if in _initial mode_. + pub fn is_initial(&self) -> bool { + matches!(self.inner, Inner::Initial(_)) + } + + /// Returns a mutable reference to the latest config. + pub fn config_mut(&mut self) -> &mut CloneableLayer { + match &mut self.inner { + Inner::Initial(initial) => initial.config, + Inner::Override(overrid) => overrid.config, + } + } + + /// Returns a mutable reference to the latest runtime components. + pub fn runtime_components_mut(&mut self) -> &mut RuntimeComponentsBuilder { + match &mut self.inner { + Inner::Initial(initial) => initial.components, + Inner::Override(overrid) => overrid.components, + } + } + + /// Returns true if the latest config has `T` set. + /// + /// The "latest" is initial for `Resolver::Initial`, and override for `Resolver::Override`. + pub fn is_latest_set(&self) -> bool + where + T: Storable>, + { + self.config().load::().is_some() + } + + /// Returns true if `T` is set anywhere. + pub fn is_set(&self) -> bool + where + T: Storable>, + { + match &self.inner { + Inner::Initial(initial) => initial.config.load::().is_some(), + Inner::Override(overrid) => { + overrid.initial_config.load::().is_some() || overrid.config.load::().is_some() + } + } + } + + /// Resolves the value `T` with fallback + pub fn resolve_config(&self) -> ::ReturnedType<'_> + where + T: Storable>, + { + let mut maybe_value = self.config().load::(); + if maybe_value.is_none() { + // Try to fallback + if let Inner::Override(overrid) = &self.inner { + maybe_value = overrid.initial_config.load::() + } + } + maybe_value + } + + // Add additional component methods as needed + component!( + SharedAsyncSleep, + sleep_impl, + latest_sleep_impl, + "The async sleep implementation." + ); + + fn config(&self) -> &Layer { + match &self.inner { + Inner::Initial(initial) => initial.config, + Inner::Override(overrid) => overrid.config, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_types::config_bag::CloneableLayer; + + #[derive(Clone, Debug)] + struct TestStorable(String); + impl Storable for TestStorable { + type Storer = StoreReplace; + } + + #[test] + fn initial_mode_config() { + let mut config = CloneableLayer::new("test"); + let mut components = RuntimeComponentsBuilder::new("test"); + + let mut resolver = Resolver::initial(&mut config, &mut components); + assert!(resolver.is_initial()); + assert!(!resolver.is_latest_set::()); + assert!(!resolver.is_set::()); + assert!(resolver.resolve_config::().is_none()); + + resolver.config_mut().store_put(TestStorable("test".into())); + assert!(resolver.is_latest_set::()); + assert!(resolver.is_set::()); + assert_eq!("test", resolver.resolve_config::().unwrap().0); + } + + #[test] + fn override_mode_config() { + let mut initial_config = CloneableLayer::new("initial"); + let initial_components = RuntimeComponentsBuilder::new("initial"); + let mut config = CloneableLayer::new("override"); + let mut components = RuntimeComponentsBuilder::new("override"); + + let resolver = Resolver::overrid( + initial_config.clone().freeze(), + &initial_components, + &mut config, + &mut components, + ); + assert!(!resolver.is_initial()); + assert!(!resolver.is_latest_set::()); + assert!(!resolver.is_set::()); + assert!(resolver.resolve_config::().is_none()); + + initial_config.store_put(TestStorable("test".into())); + let resolver = Resolver::overrid( + initial_config.clone().freeze(), + &initial_components, + &mut config, + &mut components, + ); + assert!(!resolver.is_latest_set::()); + assert!(resolver.is_set::()); + assert_eq!("test", resolver.resolve_config::().unwrap().0); + + initial_config.unset::(); + config.store_put(TestStorable("test".into())); + let resolver = Resolver::overrid( + initial_config.clone().freeze(), + &initial_components, + &mut config, + &mut components, + ); + assert!(resolver.is_latest_set::()); + assert!(resolver.is_set::()); + assert_eq!("test", resolver.resolve_config::().unwrap().0); + + initial_config.store_put(TestStorable("override me".into())); + config.store_put(TestStorable("override".into())); + let resolver = Resolver::overrid( + initial_config.freeze(), + &initial_components, + &mut config, + &mut components, + ); + assert!(resolver.is_latest_set::()); + assert!(resolver.is_set::()); + assert_eq!( + "override", + resolver.resolve_config::().unwrap().0 + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors.rs new file mode 100644 index 0000000000..75da05271e --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors.rs @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Interceptor for connection poisoning. +pub mod connection_poisoning; + +#[cfg(feature = "test-util")] +pub mod test_util; + +// TODO(enableNewSmithyRuntimeCleanup): Delete this module +/// Unstable API for interfacing the old middleware connectors with the newer orchestrator connectors. +/// +/// Important: This module and its contents will be removed in the next release. +pub mod adapter { + use aws_smithy_client::erase::DynConnector; + use aws_smithy_runtime_api::client::connectors::HttpConnector; + use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; + use std::sync::{Arc, Mutex}; + + /// Adapts a [`DynConnector`] to the [`HttpConnector`] trait. + /// + /// This is a temporary adapter that allows the old-style tower-based connectors to + /// work with the new non-tower based architecture of the generated clients. + /// It will be removed in a future release. + #[derive(Debug)] + pub struct DynConnectorAdapter { + // `DynConnector` requires `&mut self`, so we need interior mutability to adapt to it + dyn_connector: Arc>, + } + + impl DynConnectorAdapter { + /// Creates a new `DynConnectorAdapter`. + pub fn new(dyn_connector: DynConnector) -> Self { + Self { + dyn_connector: Arc::new(Mutex::new(dyn_connector)), + } + } + } + + impl HttpConnector for DynConnectorAdapter { + fn call(&self, request: HttpRequest) -> BoxFuture { + let future = self.dyn_connector.lock().unwrap().call_lite(request); + future + } + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs new file mode 100644 index 0000000000..5f6f4e7862 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs @@ -0,0 +1,145 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::connection::{CaptureSmithyConnection, ConnectionMetadata}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeDeserializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, +}; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::retry::{ErrorKind, ReconnectMode, RetryConfig}; +use std::fmt; +use tracing::{debug, error}; + +/// A interceptor for poisoning connections in response to certain events. +/// +/// This interceptor, when paired with a compatible connection, allows the connection to be +/// poisoned in reaction to certain events *(like receiving a transient error.)* This allows users +/// to avoid sending requests to a server that isn't responding. This can increase the load on a +/// server, because more connections will be made overall. +/// +/// **In order for this interceptor to work,** the configured connection must interact with the +/// "connection retriever" stored in an HTTP request's `extensions` map. For an example of this, +/// see [aws_smithy_client::hyper_ext::Adapter](https://github.com/awslabs/smithy-rs/blob/47b3d23ff3cabd67e797af616101f5a4ea6be5e8/rust-runtime/aws-smithy-client/src/hyper_ext.rs#L155). +/// When a connection is made available to the retriever, this interceptor will call a `.poison` +/// method on it, signalling that the connection should be dropped. It is up to the connection +/// implementer to handle this. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct ConnectionPoisoningInterceptor {} + +impl ConnectionPoisoningInterceptor { + /// Create a new `ConnectionPoisoningInterceptor`. + pub fn new() -> Self { + Self::default() + } +} + +impl Interceptor for ConnectionPoisoningInterceptor { + fn name(&self) -> &'static str { + "ConnectionPoisoningInterceptor" + } + + fn modify_before_transmit( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let capture_smithy_connection = CaptureSmithyConnectionWrapper::new(); + context + .request_mut() + .extensions_mut() + .insert(capture_smithy_connection.clone_inner()); + cfg.interceptor_state().store_put(capture_smithy_connection); + + Ok(()) + } + + fn modify_before_deserialization( + &self, + context: &mut BeforeDeserializationInterceptorContextMut<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let reconnect_mode = cfg + .load::() + .map(RetryConfig::reconnect_mode) + .unwrap_or(ReconnectMode::ReconnectOnTransientError); + let captured_connection = cfg.load::().cloned(); + let retry_classifiers = runtime_components + .retry_classifiers() + .ok_or("retry classifiers are required for connection poisoning to work")?; + + let error_is_transient = retry_classifiers + .classify_retry(context.inner_mut()) + .map(|reason| reason == RetryReason::Error(ErrorKind::TransientError)) + .unwrap_or_default(); + let connection_poisoning_is_enabled = + reconnect_mode == ReconnectMode::ReconnectOnTransientError; + + if error_is_transient && connection_poisoning_is_enabled { + debug!("received a transient error, poisoning the connection..."); + + if let Some(captured_connection) = captured_connection.and_then(|conn| conn.get()) { + captured_connection.poison(); + debug!("the connection was poisoned") + } else { + error!( + "unable to poison the connection because no connection was found! The underlying HTTP connector never set a connection." + ); + } + } + + Ok(()) + } +} + +// TODO(enableNewSmithyRuntimeCleanup): A storable wrapper won't be needed anymore once we absorb aws_smithy_http into the new runtime crate. +/// A wrapper around CaptureSmithyConnection that implements `Storable` so that it can be added to the `ConfigBag`. +#[derive(Clone, Default)] +pub struct CaptureSmithyConnectionWrapper { + inner: CaptureSmithyConnection, +} + +impl CaptureSmithyConnectionWrapper { + /// Creates a new `CaptureSmithyConnectionWrapper`. + pub fn new() -> Self { + Self { + inner: CaptureSmithyConnection::new(), + } + } + + /// Returns a reference to the inner `CaptureSmithyConnection`. + pub fn clone_inner(&self) -> CaptureSmithyConnection { + self.inner.clone() + } + + /// Returns the captured connection metadata, if any. + pub fn get(&self) -> Option { + self.inner.get() + } + + /// Sets the connection retriever function. + pub fn set_connection_retriever(&self, f: F) + where + F: Fn() -> Option + Send + Sync + 'static, + { + self.inner.set_connection_retriever(f) + } +} + +impl Storable for CaptureSmithyConnectionWrapper { + type Storer = StoreReplace; +} + +impl fmt::Debug for CaptureSmithyConnectionWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CaptureSmithyConnectionWrapper") + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs similarity index 51% rename from rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs rename to rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs index eb3c29daa6..c7afd868d4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs @@ -5,18 +5,17 @@ //! Module with client connectors useful for testing. +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; -use aws_smithy_runtime_api::client::orchestrator::{ - BoxFallibleFut, Connection, HttpRequest, HttpResponse, -}; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_runtime_api::client::connectors::HttpConnector; +use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; use http::header::{HeaderName, CONTENT_TYPE}; use std::fmt::Debug; -use std::future::ready; use std::ops::Deref; use std::sync::{Arc, Mutex}; +use std::time::Duration; use tokio::sync::oneshot; /// Test Connection to capture a single request @@ -93,29 +92,77 @@ pub fn capture_request( ) } -type ConnectVec = Vec<(HttpRequest, HttpResponse)>; +type ConnectionEvents = Vec; +/// Test data for the [`TestConnector`]. +/// +/// Each `ConnectionEvent` represents one HTTP request and response +/// through the connector. Optionally, a latency value can be set to simulate +/// network latency (done via async sleep in the `TestConnector`). #[derive(Debug)] -pub struct ValidateRequest { - pub expected: HttpRequest, - pub actual: HttpRequest, +pub struct ConnectionEvent { + latency: Duration, + req: HttpRequest, + res: HttpResponse, +} + +impl ConnectionEvent { + /// Creates a new `ConnectionEvent`. + pub fn new(req: HttpRequest, res: HttpResponse) -> Self { + Self { + res, + req, + latency: Duration::from_secs(0), + } + } + + /// Add simulated latency to this `ConnectionEvent` + pub fn with_latency(mut self, latency: Duration) -> Self { + self.latency = latency; + self + } + + /// Returns the test request. + pub fn request(&self) -> &HttpRequest { + &self.req + } + + /// Returns the test response. + pub fn response(&self) -> &HttpResponse { + &self.res + } +} + +impl From<(HttpRequest, HttpResponse)> for ConnectionEvent { + fn from((req, res): (HttpRequest, HttpResponse)) -> Self { + Self::new(req, res) + } +} + +#[derive(Debug)] +struct ValidateRequest { + expected: HttpRequest, + actual: HttpRequest, } impl ValidateRequest { - pub fn assert_matches(&self, ignore_headers: &[HeaderName]) { + fn assert_matches(&self, index: usize, ignore_headers: &[HeaderName]) { let (actual, expected) = (&self.actual, &self.expected); - assert_eq!(actual.uri(), expected.uri()); + assert_eq!( + actual.uri(), + expected.uri(), + "Request #{index} - URI doesn't match expected value" + ); for (name, value) in expected.headers() { if !ignore_headers.contains(name) { let actual_header = actual .headers() .get(name) - .unwrap_or_else(|| panic!("Header {:?} missing", name)); + .unwrap_or_else(|| panic!("Request #{index} - Header {name:?} is missing")); assert_eq!( actual_header.to_str().unwrap(), value.to_str().unwrap(), - "Header mismatch for {:?}", - name + "Request #{index} - Header {name:?} doesn't match expected value", ); } } @@ -133,90 +180,85 @@ impl ValidateRequest { }; match (actual_str, expected_str) { (Ok(actual), Ok(expected)) => assert_ok(validate_body(actual, expected, media_type)), - _ => assert_eq!(actual.body().bytes(), expected.body().bytes()), + _ => assert_eq!( + actual.body().bytes(), + expected.body().bytes(), + "Request #{index} - Body contents didn't match expected value" + ), }; } } -/// TestConnection for use as a [`Connection`]. +/// Test connector for use as a [`HttpConnector`]. /// /// A basic test connection. It will: /// - Respond to requests with a preloaded series of responses /// - Record requests for future examination -#[derive(Debug)] -pub struct TestConnection { - data: Arc>, +#[derive(Debug, Clone)] +pub struct TestConnector { + data: Arc>, requests: Arc>>, + sleep_impl: SharedAsyncSleep, } -// Need a clone impl that ignores `B` -impl Clone for TestConnection { - fn clone(&self) -> Self { - TestConnection { - data: self.data.clone(), - requests: self.requests.clone(), - } - } -} - -impl TestConnection { - pub fn new(mut data: ConnectVec) -> Self { +impl TestConnector { + /// Creates a new test connector. + pub fn new(mut data: ConnectionEvents, sleep_impl: impl Into) -> Self { data.reverse(); - TestConnection { + TestConnector { data: Arc::new(Mutex::new(data)), requests: Default::default(), + sleep_impl: sleep_impl.into(), } } - pub fn requests(&self) -> impl Deref> + '_ { + fn requests(&self) -> impl Deref> + '_ { self.requests.lock().unwrap() } + /// Asserts the expected requests match the actual requests. + /// + /// The expected requests are given as the connection events when the `TestConnector` + /// is created. The `TestConnector` will record the actual requests and assert that + /// they match the expected requests. + /// + /// A list of headers that should be ignored when comparing requests can be passed + /// for cases where headers are non-deterministic or are irrelevant to the test. #[track_caller] pub fn assert_requests_match(&self, ignore_headers: &[HeaderName]) { - for req in self.requests().iter() { - req.assert_matches(ignore_headers) + for (i, req) in self.requests().iter().enumerate() { + req.assert_matches(i, ignore_headers) } - let remaining_requests = self.data.lock().unwrap().len(); + let remaining_requests = self.data.lock().unwrap(); + let number_of_remaining_requests = remaining_requests.len(); let actual_requests = self.requests().len(); - assert_eq!( - remaining_requests, 0, - "Expected {} additional requests ({} were made)", - remaining_requests, actual_requests + assert!( + remaining_requests.is_empty(), + "Expected {number_of_remaining_requests} additional requests (only {actual_requests} sent)", ); } } -impl Connection for TestConnection { - fn call(&self, request: &mut HttpRequest, _cfg: &ConfigBag) -> BoxFallibleFut { - // TODO(orchestrator) Validate request - - let res = if let Some((expected, resp)) = self.data.lock().unwrap().pop() { - let actual = try_clone_http_request(request).expect("test request is cloneable"); - self.requests - .lock() - .unwrap() - .push(ValidateRequest { expected, actual }); - Ok(resp.map(SdkBody::from)) +impl HttpConnector for TestConnector { + fn call(&self, request: HttpRequest) -> BoxFuture { + let (res, simulated_latency) = if let Some(event) = self.data.lock().unwrap().pop() { + self.requests.lock().unwrap().push(ValidateRequest { + expected: event.req, + actual: request, + }); + + (Ok(event.res.map(SdkBody::from)), event.latency) } else { - Err(ConnectorError::other("No more data".into(), None).into()) + ( + Err(ConnectorError::other("No more data".into(), None).into()), + Duration::from_secs(0), + ) }; - Box::pin(ready(res)) + let sleep = self.sleep_impl.sleep(simulated_latency); + Box::pin(async move { + sleep.await; + res + }) } } - -pub fn try_clone_http_request(req: &http::Request) -> Option> { - let cloned_body = req.body().try_clone()?; - let mut cloned_request = http::Request::builder() - .uri(req.uri().clone()) - .method(req.method()); - *cloned_request - .headers_mut() - .expect("builder has not been modified, headers must be valid") = req.headers().clone(); - let req = cloned_request - .body(cloned_body) - .expect("a clone of a valid request should be a valid request"); - - Some(req) -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity.rs b/rust-runtime/aws-smithy-runtime/src/client/identity.rs new file mode 100644 index 0000000000..7aaef9c3ca --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/identity.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Identity resolver implementation for "no auth". +pub mod no_auth; diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs new file mode 100644 index 0000000000..01121eab90 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs @@ -0,0 +1,36 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; +use aws_smithy_runtime_api::client::orchestrator::Future; +use aws_smithy_types::config_bag::ConfigBag; + +/// Identity for the [`NoAuthScheme`](crate::client::auth::no_auth::NoAuthScheme) auth scheme. +#[derive(Debug, Default)] +pub struct NoAuthIdentity; + +impl NoAuthIdentity { + /// Creates a new `NoAuthIdentity`. + pub fn new() -> Self { + Self + } +} + +/// Identity resolver for the [`NoAuthScheme`](crate::client::auth::no_auth::NoAuthScheme) auth scheme. +#[derive(Debug, Default)] +pub struct NoAuthIdentityResolver; + +impl NoAuthIdentityResolver { + /// Creates a new `NoAuthIdentityResolver`. + pub fn new() -> Self { + Self + } +} + +impl IdentityResolver for NoAuthIdentityResolver { + fn resolve_identity(&self, _: &ConfigBag) -> Future { + Future::ready(Ok(Identity::new(NoAuthIdentity::new(), None))) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs new file mode 100644 index 0000000000..c9dd074605 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs @@ -0,0 +1,450 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::body::SdkBody; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, + FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, +}; +use aws_smithy_runtime_api::client::interceptors::context::{ + Error, Input, InterceptorContext, Output, +}; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorError, SharedInterceptor, +}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::error::display::DisplayErrorContext; +use std::error::Error as StdError; +use std::fmt; +use std::marker::PhantomData; + +macro_rules! interceptor_impl_fn { + (mut $interceptor:ident) => { + pub(crate) fn $interceptor( + self, + ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!(concat!( + "running `", + stringify!($interceptor), + "` interceptors" + )); + let mut result: Result<(), (&str, BoxError)> = Ok(()); + let mut ctx = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.$interceptor(&mut ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!( + "{}::{}: {}", + last_error.0, + stringify!($interceptor), + DisplayErrorContext(&*last_error.1) + ); + } + result = Err((interceptor.name(), new_error)); + } + } + } + result.map_err(|(name, err)| InterceptorError::$interceptor(name, err)) + } + }; + (ref $interceptor:ident) => { + pub(crate) fn $interceptor( + self, + ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), (&str, BoxError)> = Ok(()); + let ctx = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.$interceptor(&ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!( + "{}::{}: {}", + last_error.0, + stringify!($interceptor), + DisplayErrorContext(&*last_error.1) + ); + } + result = Err((interceptor.name(), new_error)); + } + } + } + result.map_err(|(name, err)| InterceptorError::$interceptor(name, err)) + } + }; +} + +#[derive(Debug)] +pub(crate) struct Interceptors { + interceptors: I, +} + +impl Interceptors +where + I: Iterator, +{ + pub(crate) fn new(interceptors: I) -> Self { + Self { interceptors } + } + + fn into_iter(self) -> impl Iterator { + self.interceptors.map(ConditionallyEnabledInterceptor) + } + + pub(crate) fn read_before_execution( + self, + operation: bool, + ctx: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!( + "running {} `read_before_execution` interceptors", + if operation { "operation" } else { "client" } + ); + let mut result: Result<(), (&str, BoxError)> = Ok(()); + let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!( + "{}::{}: {}", + last_error.0, + "read_before_execution", + DisplayErrorContext(&*last_error.1) + ); + } + result = Err((interceptor.name(), new_error)); + } + } + } + result.map_err(|(name, err)| InterceptorError::read_before_execution(name, err)) + } + + interceptor_impl_fn!(mut modify_before_serialization); + interceptor_impl_fn!(ref read_before_serialization); + interceptor_impl_fn!(ref read_after_serialization); + interceptor_impl_fn!(mut modify_before_retry_loop); + interceptor_impl_fn!(ref read_before_attempt); + interceptor_impl_fn!(mut modify_before_signing); + interceptor_impl_fn!(ref read_before_signing); + interceptor_impl_fn!(ref read_after_signing); + interceptor_impl_fn!(mut modify_before_transmit); + interceptor_impl_fn!(ref read_before_transmit); + interceptor_impl_fn!(ref read_after_transmit); + interceptor_impl_fn!(mut modify_before_deserialization); + interceptor_impl_fn!(ref read_before_deserialization); + interceptor_impl_fn!(ref read_after_deserialization); + + pub(crate) fn modify_before_attempt_completion( + self, + ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!("running `modify_before_attempt_completion` interceptors"); + let mut result: Result<(), (&str, BoxError)> = Ok(()); + let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.modify_before_attempt_completion(&mut ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!( + "{}::{}: {}", + last_error.0, + "modify_before_attempt_completion", + DisplayErrorContext(&*last_error.1) + ); + } + result = Err((interceptor.name(), new_error)); + } + } + } + result.map_err(|(name, err)| InterceptorError::modify_before_attempt_completion(name, err)) + } + + pub(crate) fn read_after_attempt( + self, + ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!("running `read_after_attempt` interceptors"); + let mut result: Result<(), (&str, BoxError)> = Ok(()); + let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.read_after_attempt(&ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!( + "{}::{}: {}", + last_error.0, + "read_after_attempt", + DisplayErrorContext(&*last_error.1) + ); + } + result = Err((interceptor.name(), new_error)); + } + } + } + result.map_err(|(name, err)| InterceptorError::read_after_attempt(name, err)) + } + + pub(crate) fn modify_before_completion( + self, + ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!("running `modify_before_completion` interceptors"); + let mut result: Result<(), (&str, BoxError)> = Ok(()); + let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.modify_before_completion(&mut ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!( + "{}::{}: {}", + last_error.0, + "modify_before_completion", + DisplayErrorContext(&*last_error.1) + ); + } + result = Err((interceptor.name(), new_error)); + } + } + } + result.map_err(|(name, err)| InterceptorError::modify_before_completion(name, err)) + } + + pub(crate) fn read_after_execution( + self, + ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!("running `read_after_execution` interceptors"); + let mut result: Result<(), (&str, BoxError)> = Ok(()); + let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.read_after_execution(&ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!( + "{}::{}: {}", + last_error.0, + "read_after_execution", + DisplayErrorContext(&*last_error.1) + ); + } + result = Err((interceptor.name(), new_error)); + } + } + } + result.map_err(|(name, err)| InterceptorError::read_after_execution(name, err)) + } +} + +/// A interceptor wrapper to conditionally enable the interceptor based on +/// [`DisableInterceptor`](aws_smithy_runtime_api::client::interceptors::DisableInterceptor) +struct ConditionallyEnabledInterceptor(SharedInterceptor); +impl ConditionallyEnabledInterceptor { + fn if_enabled(&self, cfg: &ConfigBag) -> Option<&dyn Interceptor> { + if self.0.enabled(cfg) { + Some(self.0.as_ref()) + } else { + None + } + } +} + +/// Interceptor that maps the request with a given function. +pub struct MapRequestInterceptor { + f: F, + _phantom: PhantomData, +} + +impl fmt::Debug for MapRequestInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MapRequestInterceptor") + } +} + +impl MapRequestInterceptor { + /// Creates a new `MapRequestInterceptor`. + pub fn new(f: F) -> Self { + Self { + f, + _phantom: PhantomData, + } + } +} + +impl Interceptor for MapRequestInterceptor +where + F: Fn(HttpRequest) -> Result + Send + Sync + 'static, + E: StdError + Send + Sync + 'static, +{ + fn name(&self) -> &'static str { + "MapRequestInterceptor" + } + + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let mut request = HttpRequest::new(SdkBody::taken()); + std::mem::swap(&mut request, context.request_mut()); + let mut mapped = (self.f)(request)?; + std::mem::swap(&mut mapped, context.request_mut()); + + Ok(()) + } +} + +/// Interceptor that mutates the request with a given function. +pub struct MutateRequestInterceptor { + f: F, +} + +impl fmt::Debug for MutateRequestInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MutateRequestInterceptor") + } +} + +impl MutateRequestInterceptor { + /// Creates a new `MutateRequestInterceptor`. + pub fn new(f: F) -> Self { + Self { f } + } +} + +impl Interceptor for MutateRequestInterceptor +where + F: Fn(&mut HttpRequest) + Send + Sync + 'static, +{ + fn name(&self) -> &'static str { + "MutateRequestInterceptor" + } + + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut(); + (self.f)(request); + + Ok(()) + } +} + +#[cfg(all(test, feature = "test-util"))] +mod tests { + use super::*; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeTransmitInterceptorContextRef, Input, InterceptorContext, + }; + use aws_smithy_runtime_api::client::interceptors::{ + disable_interceptor, Interceptor, SharedInterceptor, + }; + use aws_smithy_runtime_api::client::runtime_components::{ + RuntimeComponents, RuntimeComponentsBuilder, + }; + use aws_smithy_types::config_bag::ConfigBag; + + #[derive(Debug)] + struct TestInterceptor; + impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + } + + #[test] + fn test_disable_interceptors() { + #[derive(Debug)] + struct PanicInterceptor; + impl Interceptor for PanicInterceptor { + fn name(&self) -> &'static str { + "PanicInterceptor" + } + + fn read_before_transmit( + &self, + _context: &BeforeTransmitInterceptorContextRef<'_>, + _rc: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + Err("boom".into()) + } + } + let rc = RuntimeComponentsBuilder::for_tests() + .with_interceptor(SharedInterceptor::new(PanicInterceptor)) + .with_interceptor(SharedInterceptor::new(TestInterceptor)) + .build() + .unwrap(); + + let mut cfg = ConfigBag::base(); + let interceptors = Interceptors::new(rc.interceptors()); + assert_eq!( + interceptors + .into_iter() + .filter(|i| i.if_enabled(&cfg).is_some()) + .count(), + 2 + ); + + Interceptors::new(rc.interceptors()) + .read_before_transmit( + &InterceptorContext::new(Input::doesnt_matter()), + &rc, + &mut cfg, + ) + .expect_err("interceptor returns error"); + cfg.interceptor_state() + .store_put(disable_interceptor::("test")); + assert_eq!( + Interceptors::new(rc.interceptors()) + .into_iter() + .filter(|i| i.if_enabled(&cfg).is_some()) + .count(), + 1 + ); + // shouldn't error because interceptors won't run + Interceptors::new(rc.interceptors()) + .read_before_transmit( + &InterceptorContext::new(Input::doesnt_matter()), + &rc, + &mut cfg, + ) + .expect("interceptor is now disabled"); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index d97ca1cdf5..22bee36367 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -3,159 +3,1282 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(msrvUpgrade): This can be removed once we upgrade the MSRV to Rust 1.69 +#![allow(unknown_lints)] + use self::auth::orchestrate_auth; +use crate::client::interceptors::Interceptors; +use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; -use crate::client::orchestrator::phase::Phase; +use crate::client::timeout::{MaybeTimeout, MaybeTimeoutConfig, TimeoutKind}; +use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; -use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Interceptors}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::connectors::HttpConnector; +use aws_smithy_runtime_api::client::interceptors::context::{ + Error, Input, InterceptorContext, Output, RewindResult, +}; use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, HttpRequest, HttpResponse, + HttpResponse, LoadedRequestBody, OrchestratorError, }; +use aws_smithy_runtime_api::client::retries::{RequestAttempts, RetryStrategy, ShouldAttempt}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; -use aws_smithy_runtime_api::config_bag::ConfigBag; -use tracing::{debug_span, Instrument}; +use aws_smithy_runtime_api::client::ser_de::{ + RequestSerializer, ResponseDeserializer, SharedRequestSerializer, SharedResponseDeserializer, +}; +use aws_smithy_types::config_bag::ConfigBag; +use std::mem; +use tracing::{debug, debug_span, instrument, trace, Instrument}; mod auth; +/// Defines types that implement a trait for endpoint resolution +pub mod endpoints; mod http; -pub(self) mod phase; +macro_rules! halt { + ([$ctx:ident] => $err:expr) => {{ + debug!("encountered orchestrator error; halting"); + $ctx.fail($err.into()); + return; + }}; +} + +macro_rules! halt_on_err { + ([$ctx:ident] => $expr:expr) => { + match $expr { + Ok(ok) => ok, + Err(err) => halt!([$ctx] => err), + } + }; +} + +macro_rules! continue_on_err { + ([$ctx:ident] => $expr:expr) => { + if let Err(err) = $expr { + debug!(err = ?err, "encountered orchestrator error; continuing"); + $ctx.fail(err.into()); + } + }; +} + +macro_rules! run_interceptors { + (continue_on_err: { $($interceptor:ident($ctx:ident, $rc:ident, $cfg:ident);)+ }) => { + $(run_interceptors!(continue_on_err: $interceptor($ctx, $rc, $cfg));)+ + }; + (continue_on_err: $interceptor:ident($ctx:ident, $rc:ident, $cfg:ident)) => { + continue_on_err!([$ctx] => run_interceptors!(__private $interceptor($ctx, $rc, $cfg))) + }; + (halt_on_err: { $($interceptor:ident($ctx:ident, $rc:ident, $cfg:ident);)+ }) => { + $(run_interceptors!(halt_on_err: $interceptor($ctx, $rc, $cfg));)+ + }; + (halt_on_err: $interceptor:ident($ctx:ident, $rc:ident, $cfg:ident)) => { + halt_on_err!([$ctx] => run_interceptors!(__private $interceptor($ctx, $rc, $cfg))) + }; + (__private $interceptor:ident($ctx:ident, $rc:ident, $cfg:ident)) => { + Interceptors::new($rc.interceptors()).$interceptor($ctx, $rc, $cfg) + }; +} + +/// Orchestrates the execution of a request and handling of a response. +/// +/// The given `runtime_plugins` will be used to generate a `ConfigBag` for this request, +/// and then the given `input` will be serialized and transmitted. When a response is +/// received, it will be deserialized and returned. +/// +/// This orchestration handles retries, endpoint resolution, identity resolution, and signing. +/// Each of these are configurable via the config and runtime components given by the runtime +/// plugins. pub async fn invoke( + service_name: &str, + operation_name: &str, input: Input, runtime_plugins: &RuntimePlugins, ) -> Result> { - let mut cfg = ConfigBag::base(); - let cfg = &mut cfg; - let mut interceptors = Interceptors::new(); - let interceptors = &mut interceptors; - - let context = Phase::construction(InterceptorContext::new(input)) - // Client configuration - .include(|_| runtime_plugins.apply_client_configuration(cfg))? - .include(|ctx| interceptors.client_read_before_execution(ctx, cfg))? - // Operation configuration - .include(|_| runtime_plugins.apply_operation_configuration(cfg))? - .include(|ctx| interceptors.operation_read_before_execution(ctx, cfg))? - // Before serialization - .include(|ctx| interceptors.read_before_serialization(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_serialization(ctx, cfg))? - // Serialization - .include_mut(|ctx| { - let request_serializer = cfg.request_serializer(); - let request = request_serializer - .serialize_input(ctx.take_input().expect("input set at this point"))?; - ctx.set_request(request); - Result::<(), BoxError>::Ok(()) - })? - // After serialization - .include(|ctx| interceptors.read_after_serialization(ctx, cfg))? - // Before retry loop - .include_mut(|ctx| interceptors.modify_before_retry_loop(ctx, cfg))? - .finish(); + invoke_with_stop_point( + service_name, + operation_name, + input, + runtime_plugins, + StopPoint::None, + ) + .await? + .finalize() +} + +/// Allows for returning early at different points during orchestration. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum StopPoint { + /// Don't stop orchestration early + None, + + /// Stop the orchestrator before transmitting the request + BeforeTransmit, +} + +/// Same as [`invoke`], but allows for returning early at different points during orchestration. +/// +/// Orchestration will cease at the point specified by `stop_point`. This is useful for orchestrations +/// that don't need to actually transmit requests, such as for generating presigned requests. +/// +/// See the docs on [`invoke`] for more details. +pub async fn invoke_with_stop_point( + service_name: &str, + operation_name: &str, + input: Input, + runtime_plugins: &RuntimePlugins, + stop_point: StopPoint, +) -> Result> { + async move { + let mut cfg = ConfigBag::base(); + let cfg = &mut cfg; + + let mut ctx = InterceptorContext::new(input); + + let runtime_components = apply_configuration(&mut ctx, cfg, runtime_plugins) + .map_err(SdkError::construction_failure)?; + trace!(runtime_components = ?runtime_components); + let operation_timeout_config = + MaybeTimeoutConfig::new(&runtime_components, cfg, TimeoutKind::Operation); + trace!(operation_timeout_config = ?operation_timeout_config); + async { + // If running the pre-execution interceptors failed, then we skip running the op and run the + // final interceptors instead. + if !ctx.is_failed() { + try_op(&mut ctx, cfg, &runtime_components, stop_point).await; + } + finally_op(&mut ctx, cfg, &runtime_components).await; + Ok(ctx) + } + .maybe_timeout(operation_timeout_config) + .await + } + .instrument(debug_span!("invoke", service = %service_name, operation = %operation_name)) + .await +} + +/// Apply configuration is responsible for apply runtime plugins to the config bag, as well as running +/// `read_before_execution` interceptors. If a failure occurs due to config construction, `invoke` +/// will raise it to the user. If an interceptor fails, then `invoke` +#[instrument(skip_all)] +fn apply_configuration( + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + runtime_plugins: &RuntimePlugins, +) -> Result { + let client_rc_builder = runtime_plugins.apply_client_configuration(cfg)?; + continue_on_err!([ctx] => Interceptors::new(client_rc_builder.interceptors()).read_before_execution(false, ctx, cfg)); + + let operation_rc_builder = runtime_plugins.apply_operation_configuration(cfg)?; + continue_on_err!([ctx] => Interceptors::new(operation_rc_builder.interceptors()).read_before_execution(true, ctx, cfg)); + + // The order below is important. Client interceptors must run before operation interceptors. + Ok(RuntimeComponents::builder("merged orchestrator components") + .merge_from(&client_rc_builder) + .merge_from(&operation_rc_builder) + .build()?) +} + +#[instrument(skip_all)] +async fn try_op( + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + runtime_components: &RuntimeComponents, + stop_point: StopPoint, +) { + // Before serialization + run_interceptors!(halt_on_err: { + read_before_serialization(ctx, runtime_components, cfg); + modify_before_serialization(ctx, runtime_components, cfg); + }); + + // Serialization + ctx.enter_serialization_phase(); { - let retry_strategy = cfg.retry_strategy(); - match retry_strategy.should_attempt_initial_request(cfg) { - // Yes, let's make a request - Ok(_) => {} - // No, we shouldn't make a request because... - Err(err) => return Err(Phase::dispatch(context).fail(err)), - } - } - - let mut context = context; - let handling_phase = loop { - let dispatch_phase = Phase::dispatch(context); - context = make_an_attempt(dispatch_phase, cfg, interceptors) - .await? - .include(|ctx| interceptors.read_after_attempt(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_attempt_completion(ctx, cfg))? - .finish(); - - let retry_strategy = cfg.retry_strategy(); - match retry_strategy.should_attempt_retry(&context, cfg) { + let _span = debug_span!("serialization").entered(); + let request_serializer = cfg + .load::() + .expect("request serializer must be in the config bag") + .clone(); + let input = ctx.take_input().expect("input set at this point"); + let request = halt_on_err!([ctx] => request_serializer.serialize_input(input, cfg).map_err(OrchestratorError::other)); + ctx.set_request(request); + } + + // Load the request body into memory if configured to do so + if let Some(&LoadedRequestBody::Requested) = cfg.load::() { + debug!("loading request body into memory"); + let mut body = SdkBody::taken(); + mem::swap(&mut body, ctx.request_mut().expect("set above").body_mut()); + let loaded_body = halt_on_err!([ctx] => + ByteStream::new(body).collect().await.map_err(OrchestratorError::other) + ) + .into_bytes(); + *ctx.request_mut().as_mut().expect("set above").body_mut() = + SdkBody::from(loaded_body.clone()); + cfg.interceptor_state() + .store_put(LoadedRequestBody::Loaded(loaded_body)); + } + + // Before transmit + ctx.enter_before_transmit_phase(); + run_interceptors!(halt_on_err: { + read_after_serialization(ctx, runtime_components, cfg); + modify_before_retry_loop(ctx, runtime_components, cfg); + }); + + // If we got a retry strategy from the bag, ask it what to do. + // Otherwise, assume we should attempt the initial request. + let should_attempt = runtime_components + .retry_strategy() + .should_attempt_initial_request(runtime_components, cfg); + match should_attempt { + // Yes, let's make a request + Ok(ShouldAttempt::Yes) => debug!("retry strategy has OKed initial request"), + // No, this request shouldn't be sent + Ok(ShouldAttempt::No) => { + let err: BoxError = "the retry strategy indicates that an initial request shouldn't be made, but it didn't specify why".into(); + halt!([ctx] => OrchestratorError::other(err)); + } + // No, we shouldn't make a request because... + Err(err) => halt!([ctx] => OrchestratorError::other(err)), + Ok(ShouldAttempt::YesAfterDelay(delay)) => { + let sleep_impl = halt_on_err!([ctx] => runtime_components.sleep_impl().ok_or_else(|| OrchestratorError::other( + "the retry strategy requested a delay before sending the initial request, but no 'async sleep' implementation was set" + ))); + debug!("retry strategy has OKed initial request after a {delay:?} delay"); + sleep_impl.sleep(delay).await; + } + } + + // Save a request checkpoint before we make the request. This will allow us to "rewind" + // the request in the case of retry attempts. + ctx.save_checkpoint(); + let mut retry_delay = None; + for i in 1u32.. { + // Break from the loop if we can't rewind the request's state. This will always succeed the + // first time, but will fail on subsequent iterations if the request body wasn't retryable. + trace!("checking if context can be rewound for attempt #{i}"); + if let RewindResult::Impossible = ctx.rewind(cfg) { + debug!("request cannot be retried since the request body cannot be cloned"); + break; + } + // Track which attempt we're currently on. + cfg.interceptor_state() + .store_put::(i.into()); + // Backoff time should not be included in the attempt timeout + if let Some((delay, sleep)) = retry_delay.take() { + debug!("delaying for {delay:?}"); + sleep.await; + } + let attempt_timeout_config = + MaybeTimeoutConfig::new(runtime_components, cfg, TimeoutKind::OperationAttempt); + trace!(attempt_timeout_config = ?attempt_timeout_config); + let maybe_timeout = async { + debug!("beginning attempt #{i}"); + try_attempt(ctx, cfg, runtime_components, stop_point).await; + finally_attempt(ctx, cfg, runtime_components).await; + Result::<_, SdkError>::Ok(()) + } + .maybe_timeout(attempt_timeout_config) + .await + .map_err(|err| OrchestratorError::timeout(err.into_source().unwrap())); + + // We continue when encountering a timeout error. The retry classifier will decide what to do with it. + continue_on_err!([ctx] => maybe_timeout); + + // If we got a retry strategy from the bag, ask it what to do. + // If no strategy was set, we won't retry. + let should_attempt = halt_on_err!([ctx] => runtime_components + .retry_strategy() + .should_attempt_retry(ctx, runtime_components, cfg) + .map_err(OrchestratorError::other)); + match should_attempt { // Yes, let's retry the request - Ok(true) => continue, + ShouldAttempt::Yes => continue, // No, this request shouldn't be retried - Ok(false) => {} - // I couldn't determine if the request should be retried because an error occurred. - Err(err) => { - return Err(Phase::response_handling(context).fail(err)); + ShouldAttempt::No => { + debug!("a retry is either unnecessary or not possible, exiting attempt loop"); + break; + } + ShouldAttempt::YesAfterDelay(delay) => { + let sleep_impl = halt_on_err!([ctx] => runtime_components.sleep_impl().ok_or_else(|| OrchestratorError::other( + "the retry strategy requested a delay before sending the retry request, but no 'async sleep' implementation was set" + ))); + retry_delay = Some((delay, sleep_impl.sleep(delay))); + continue; } } + } +} - let handling_phase = Phase::response_handling(context) - .include_mut(|ctx| interceptors.modify_before_completion(ctx, cfg))?; - let trace_probe = cfg.trace_probe(); - trace_probe.dispatch_events(); +#[instrument(skip_all)] +async fn try_attempt( + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + runtime_components: &RuntimeComponents, + stop_point: StopPoint, +) { + run_interceptors!(halt_on_err: read_before_attempt(ctx, runtime_components, cfg)); - break handling_phase.include(|ctx| interceptors.read_after_execution(ctx, cfg))?; - }; + halt_on_err!([ctx] => orchestrate_endpoint(ctx, runtime_components, cfg).await.map_err(OrchestratorError::other)); - handling_phase.finalize() -} + run_interceptors!(halt_on_err: { + modify_before_signing(ctx, runtime_components, cfg); + read_before_signing(ctx, runtime_components, cfg); + }); -// Making an HTTP request can fail for several reasons, but we still need to -// call lifecycle events when that happens. Therefore, we define this -// `make_an_attempt` function to make error handling simpler. -async fn make_an_attempt( - dispatch_phase: Phase, - cfg: &mut ConfigBag, - interceptors: &mut Interceptors, -) -> Result> { - let dispatch_phase = dispatch_phase - .include(|ctx| interceptors.read_before_attempt(ctx, cfg))? - .include_mut(|ctx| { - let request = ctx.request_mut().expect("request has been set"); - - let endpoint_resolver = cfg.endpoint_resolver(); - endpoint_resolver.resolve_and_apply_endpoint(request) - })? - .include_mut(|ctx| interceptors.modify_before_signing(ctx, cfg))? - .include(|ctx| interceptors.read_before_signing(ctx, cfg))?; - - let dispatch_phase = orchestrate_auth(dispatch_phase, cfg).await?; - - let mut context = dispatch_phase - .include(|ctx| interceptors.read_after_signing(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_transmit(ctx, cfg))? - .include(|ctx| interceptors.read_before_transmit(ctx, cfg))? - .finish(); + halt_on_err!([ctx] => orchestrate_auth(ctx, runtime_components, cfg).await.map_err(OrchestratorError::other)); + + run_interceptors!(halt_on_err: { + read_after_signing(ctx, runtime_components, cfg); + modify_before_transmit(ctx, runtime_components, cfg); + read_before_transmit(ctx, runtime_components, cfg); + }); + + // Return early if a stop point is set for before transmit + if let StopPoint::BeforeTransmit = stop_point { + debug!("ending orchestration early because the stop point is `BeforeTransmit`"); + return; + } // The connection consumes the request but we need to keep a copy of it // within the interceptor context, so we clone it here. - let call_result = { - let tx_req = context.request_mut().expect("request has been set"); - let connection = cfg.connection(); - connection.call(tx_req, cfg).await - }; + ctx.enter_transmit_phase(); + let response = halt_on_err!([ctx] => { + let request = ctx.take_request().expect("set during serialization"); + trace!(request = ?request, "transmitting request"); + let connector = halt_on_err!([ctx] => runtime_components.http_connector().ok_or_else(|| + OrchestratorError::other("No HTTP connector was available to send this request. \ + Enable the `rustls` crate feature or set a connector to fix this.") + )); + connector.call(request).await.map_err(|err| { + match err.downcast() { + Ok(connector_error) => OrchestratorError::connector(*connector_error), + Err(box_err) => OrchestratorError::other(box_err) + } + }) + }); + trace!(response = ?response, "received response from service"); + ctx.set_response(response); + ctx.enter_before_deserialization_phase(); - let mut context = Phase::dispatch(context) - .include_mut(move |ctx| { - ctx.set_response(call_result?); - Result::<(), BoxError>::Ok(()) - })? - .include(|ctx| interceptors.read_after_transmit(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_deserialization(ctx, cfg))? - .include(|ctx| interceptors.read_before_deserialization(ctx, cfg))? - .finish(); - - let output_or_error = { - let response = context.response_mut().expect("response has been set"); - let response_deserializer = cfg.response_deserializer(); - match response_deserializer.deserialize_streaming(response) { - Some(output_or_error) => Ok(output_or_error), + run_interceptors!(halt_on_err: { + read_after_transmit(ctx, runtime_components, cfg); + modify_before_deserialization(ctx, runtime_components, cfg); + read_before_deserialization(ctx, runtime_components, cfg); + }); + + ctx.enter_deserialization_phase(); + let output_or_error = async { + let response = ctx.response_mut().expect("set during transmit"); + let response_deserializer = cfg + .load::() + .expect("a request deserializer must be in the config bag"); + let maybe_deserialized = { + let _span = debug_span!("deserialize_streaming").entered(); + response_deserializer.deserialize_streaming(response) + }; + match maybe_deserialized { + Some(output_or_error) => output_or_error, None => read_body(response) .instrument(debug_span!("read_body")) .await - .map(|_| response_deserializer.deserialize_nonstreaming(response)), + .map_err(OrchestratorError::response) + .and_then(|_| { + let _span = debug_span!("deserialize_nonstreaming").entered(); + response_deserializer.deserialize_nonstreaming(response) + }), } + } + .instrument(debug_span!("deserialization")) + .await; + trace!(output_or_error = ?output_or_error); + ctx.set_output_or_error(output_or_error); + + ctx.enter_after_deserialization_phase(); + run_interceptors!(halt_on_err: read_after_deserialization(ctx, runtime_components, cfg)); +} + +#[instrument(skip_all)] +async fn finally_attempt( + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + runtime_components: &RuntimeComponents, +) { + run_interceptors!(continue_on_err: { + modify_before_attempt_completion(ctx, runtime_components, cfg); + read_after_attempt(ctx, runtime_components, cfg); + }); +} + +#[instrument(skip_all)] +async fn finally_op( + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + runtime_components: &RuntimeComponents, +) { + run_interceptors!(continue_on_err: { + modify_before_completion(ctx, runtime_components, cfg); + read_after_execution(ctx, runtime_components, cfg); + }); +} + +#[cfg(all(test, feature = "test-util"))] +mod tests { + use super::*; + use crate::client::auth::no_auth::{NoAuthRuntimePlugin, NO_AUTH_SCHEME_ID}; + use crate::client::orchestrator::endpoints::StaticUriEndpointResolver; + use crate::client::retries::strategy::NeverRetryStrategy; + use crate::client::test_util::{ + deserializer::CannedResponseDeserializer, serializer::CannedRequestSerializer, + }; + use ::http::{Request, Response, StatusCode}; + use aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolver; + use aws_smithy_runtime_api::client::auth::{ + AuthSchemeOptionResolverParams, SharedAuthSchemeOptionResolver, }; + use aws_smithy_runtime_api::client::connectors::{HttpConnector, SharedHttpConnector}; + use aws_smithy_runtime_api::client::endpoint::{ + EndpointResolverParams, SharedEndpointResolver, + }; + use aws_smithy_runtime_api::client::interceptors::context::{ + AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, + BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, + BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, + FinalizerInterceptorContextRef, + }; + use aws_smithy_runtime_api::client::interceptors::{Interceptor, SharedInterceptor}; + use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, Future, HttpRequest}; + use aws_smithy_runtime_api::client::retries::SharedRetryStrategy; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins}; + use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; + use std::borrow::Cow; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; + use tracing_test::traced_test; + + fn new_request_serializer() -> CannedRequestSerializer { + CannedRequestSerializer::success( + Request::builder() + .body(SdkBody::empty()) + .expect("request is valid"), + ) + } + + fn new_response_deserializer() -> CannedResponseDeserializer { + CannedResponseDeserializer::new( + Response::builder() + .status(StatusCode::OK) + .body(SdkBody::empty()) + .map_err(|err| OrchestratorError::other(Box::new(err))) + .map(Output::erase), + ) + } + + #[derive(Debug, Default)] + struct OkConnector {} + + impl OkConnector { + fn new() -> Self { + Self::default() + } + } + + impl HttpConnector for OkConnector { + fn call(&self, _request: HttpRequest) -> BoxFuture { + Box::pin(Future::ready(Ok(::http::Response::builder() + .status(200) + .body(SdkBody::empty()) + .expect("OK response is valid")))) + } + } + + #[derive(Debug)] + struct TestOperationRuntimePlugin { + builder: RuntimeComponentsBuilder, + } + + impl TestOperationRuntimePlugin { + fn new() -> Self { + Self { + builder: RuntimeComponentsBuilder::new("TestOperationRuntimePlugin") + .with_retry_strategy(Some(SharedRetryStrategy::new(NeverRetryStrategy::new()))) + .with_endpoint_resolver(Some(SharedEndpointResolver::new( + StaticUriEndpointResolver::http_localhost(8080), + ))) + .with_http_connector(Some(SharedHttpConnector::new(OkConnector::new()))) + .with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new( + StaticAuthSchemeOptionResolver::new(vec![NO_AUTH_SCHEME_ID]), + ))), + } + } + } + + impl RuntimePlugin for TestOperationRuntimePlugin { + fn config(&self) -> Option { + let mut layer = Layer::new("TestOperationRuntimePlugin"); + layer.store_put(AuthSchemeOptionResolverParams::new("idontcare")); + layer.store_put(EndpointResolverParams::new("dontcare")); + layer.store_put(SharedRequestSerializer::new(new_request_serializer())); + layer.store_put(SharedResponseDeserializer::new(new_response_deserializer())); + Some(layer.freeze()) + } + + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.builder) + } + } + + macro_rules! interceptor_error_handling_test { + (read_before_execution, $ctx:ty, $expected:expr,) => { + interceptor_error_handling_test!(__private read_before_execution, $ctx, $expected,); + }; + ($interceptor:ident, $ctx:ty, $expected:expr) => { + interceptor_error_handling_test!(__private $interceptor, $ctx, $expected, _rc: &RuntimeComponents,); + }; + (__private $interceptor:ident, $ctx:ty, $expected:expr, $($rc_arg:tt)*) => { + #[derive(Debug)] + struct FailingInterceptorA; + impl Interceptor for FailingInterceptorA { + fn name(&self) -> &'static str { "FailingInterceptorA" } + + fn $interceptor( + &self, + _ctx: $ctx, + $($rc_arg)* + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + tracing::debug!("FailingInterceptorA called!"); + Err("FailingInterceptorA".into()) + } + } + + #[derive(Debug)] + struct FailingInterceptorB; + impl Interceptor for FailingInterceptorB { + fn name(&self) -> &'static str { "FailingInterceptorB" } + + fn $interceptor( + &self, + _ctx: $ctx, + $($rc_arg)* + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + tracing::debug!("FailingInterceptorB called!"); + Err("FailingInterceptorB".into()) + } + } + + #[derive(Debug)] + struct FailingInterceptorC; + impl Interceptor for FailingInterceptorC { + fn name(&self) -> &'static str { "FailingInterceptorC" } + + fn $interceptor( + &self, + _ctx: $ctx, + $($rc_arg)* + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + tracing::debug!("FailingInterceptorC called!"); + Err("FailingInterceptorC".into()) + } + } + + #[derive(Debug)] + struct FailingInterceptorsClientRuntimePlugin(RuntimeComponentsBuilder); + impl FailingInterceptorsClientRuntimePlugin { + fn new() -> Self { + Self(RuntimeComponentsBuilder::new("test").with_interceptor(SharedInterceptor::new(FailingInterceptorA))) + } + } + impl RuntimePlugin for FailingInterceptorsClientRuntimePlugin { + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.0) + } + } - Phase::response_handling(context) - .include_mut(move |ctx| { - ctx.set_output_or_error(output_or_error?); - Result::<(), BoxError>::Ok(()) - })? - .include(|ctx| interceptors.read_after_deserialization(ctx, cfg)) + #[derive(Debug)] + struct FailingInterceptorsOperationRuntimePlugin(RuntimeComponentsBuilder); + impl FailingInterceptorsOperationRuntimePlugin { + fn new() -> Self { + Self( + RuntimeComponentsBuilder::new("test") + .with_interceptor(SharedInterceptor::new(FailingInterceptorB)) + .with_interceptor(SharedInterceptor::new(FailingInterceptorC)) + ) + } + } + impl RuntimePlugin for FailingInterceptorsOperationRuntimePlugin { + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.0) + } + } + + let input = Input::doesnt_matter(); + let runtime_plugins = RuntimePlugins::new() + .with_client_plugin(FailingInterceptorsClientRuntimePlugin::new()) + .with_operation_plugin(TestOperationRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) + .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin::new()); + let actual = invoke("test", "test", input, &runtime_plugins) + .await + .expect_err("should error"); + let actual = format!("{:?}", actual); + assert_eq!($expected, format!("{:?}", actual)); + + assert!(logs_contain("FailingInterceptorA called!")); + assert!(logs_contain("FailingInterceptorB called!")); + assert!(logs_contain("FailingInterceptorC called!")); + }; + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_execution_error_handling() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeExecution, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") } })""#.to_string(); + interceptor_error_handling_test!( + read_before_execution, + &BeforeSerializationInterceptorContextRef<'_>, + expected, + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_serialization_error_handling() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeSerialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_serialization, + &mut BeforeSerializationInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_serialization_error_handling() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeSerialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") } })""#.to_string(); + interceptor_error_handling_test!( + read_before_serialization, + &BeforeSerializationInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_serialization_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSerialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_after_serialization, + &BeforeTransmitInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_retry_loop_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeRetryLoop, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_retry_loop, + &mut BeforeTransmitInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_attempt_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeAttempt, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_before_attempt, + &BeforeTransmitInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_signing_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeSigning, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_signing, + &mut BeforeTransmitInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_signing_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeSigning, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_before_signing, + &BeforeTransmitInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_signing_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSigning, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_after_signing, + &BeforeTransmitInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_transmit_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeTransmit, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_transmit, + &mut BeforeTransmitInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_transmit_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeTransmit, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_before_transmit, + &BeforeTransmitInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_transmit_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterTransmit, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_after_transmit, + &BeforeDeserializationInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_deserialization_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeDeserialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_deserialization, + &mut BeforeDeserializationInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_deserialization_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadBeforeDeserialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_before_deserialization, + &BeforeDeserializationInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_deserialization_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterDeserialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_after_deserialization, + &AfterDeserializationInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_attempt_completion_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_attempt_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_after_attempt, + &FinalizerInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_completion_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_execution_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_after_execution, + &FinalizerInterceptorContextRef<'_>, + expected + ); + } + + macro_rules! interceptor_error_redirection_test { + (read_before_execution, $origin_ctx:ty, $destination_interceptor:ident, $destination_ctx:ty, $expected:expr) => { + interceptor_error_redirection_test!(__private read_before_execution, $origin_ctx, $destination_interceptor, $destination_ctx, $expected,); + }; + ($origin_interceptor:ident, $origin_ctx:ty, $destination_interceptor:ident, $destination_ctx:ty, $expected:expr) => { + interceptor_error_redirection_test!(__private $origin_interceptor, $origin_ctx, $destination_interceptor, $destination_ctx, $expected, _rc: &RuntimeComponents,); + }; + (__private $origin_interceptor:ident, $origin_ctx:ty, $destination_interceptor:ident, $destination_ctx:ty, $expected:expr, $($rc_arg:tt)*) => { + #[derive(Debug)] + struct OriginInterceptor; + impl Interceptor for OriginInterceptor { + fn name(&self) -> &'static str { "OriginInterceptor" } + + fn $origin_interceptor( + &self, + _ctx: $origin_ctx, + $($rc_arg)* + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + tracing::debug!("OriginInterceptor called!"); + Err("OriginInterceptor".into()) + } + } + + #[derive(Debug)] + struct DestinationInterceptor; + impl Interceptor for DestinationInterceptor { + fn name(&self) -> &'static str { "DestinationInterceptor" } + + fn $destination_interceptor( + &self, + _ctx: $destination_ctx, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + tracing::debug!("DestinationInterceptor called!"); + Err("DestinationInterceptor".into()) + } + } + + #[derive(Debug)] + struct InterceptorsTestOperationRuntimePlugin(RuntimeComponentsBuilder); + impl InterceptorsTestOperationRuntimePlugin { + fn new() -> Self { + Self( + RuntimeComponentsBuilder::new("test") + .with_interceptor(SharedInterceptor::new(OriginInterceptor)) + .with_interceptor(SharedInterceptor::new(DestinationInterceptor)) + ) + } + } + impl RuntimePlugin for InterceptorsTestOperationRuntimePlugin { + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.0) + } + } + + let input = Input::doesnt_matter(); + let runtime_plugins = RuntimePlugins::new() + .with_operation_plugin(TestOperationRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) + .with_operation_plugin(InterceptorsTestOperationRuntimePlugin::new()); + let actual = invoke("test", "test", input, &runtime_plugins) + .await + .expect_err("should error"); + let actual = format!("{:?}", actual); + assert_eq!($expected, format!("{:?}", actual)); + + assert!(logs_contain("OriginInterceptor called!")); + assert!(logs_contain("DestinationInterceptor called!")); + }; + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_execution_error_causes_jump_to_modify_before_completion() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_execution, + &BeforeSerializationInterceptorContextRef<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_serialization_error_causes_jump_to_modify_before_completion() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_serialization, + &mut BeforeSerializationInterceptorContextMut<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_serialization_error_causes_jump_to_modify_before_completion() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_serialization, + &BeforeSerializationInterceptorContextRef<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_serialization_error_causes_jump_to_modify_before_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_after_serialization, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_retry_loop_error_causes_jump_to_modify_before_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_retry_loop, + &mut BeforeTransmitInterceptorContextMut<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_attempt_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_attempt, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_signing_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_signing, + &mut BeforeTransmitInterceptorContextMut<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_signing_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_signing, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_signing_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_after_signing, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_transmit_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_transmit, + &mut BeforeTransmitInterceptorContextMut<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_transmit_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_transmit, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_transmit_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + read_after_transmit, + &BeforeDeserializationInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_deserialization_error_causes_jump_to_modify_before_attempt_completion( + ) { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_deserialization, + &mut BeforeDeserializationInterceptorContextMut<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_deserialization_error_causes_jump_to_modify_before_attempt_completion( + ) { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_deserialization, + &BeforeDeserializationInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_deserialization_error_causes_jump_to_modify_before_attempt_completion() + { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + read_after_deserialization, + &AfterDeserializationInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_attempt_completion_error_causes_jump_to_read_after_attempt() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + read_after_attempt, + &FinalizerInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_completion_error_causes_jump_to_read_after_execution() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + read_after_execution, + &FinalizerInterceptorContextRef<'_>, + expected + ); + } + + #[tokio::test] + async fn test_stop_points() { + let runtime_plugins = || { + RuntimePlugins::new() + .with_operation_plugin(TestOperationRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) + }; + + // StopPoint::None should result in a response getting set since orchestration doesn't stop + let context = invoke_with_stop_point( + "test", + "test", + Input::doesnt_matter(), + &runtime_plugins(), + StopPoint::None, + ) + .await + .expect("success"); + assert!(context.response().is_some()); + + // StopPoint::BeforeTransmit will exit right before sending the request, so there should be no response + let context = invoke_with_stop_point( + "test", + "test", + Input::doesnt_matter(), + &runtime_plugins(), + StopPoint::BeforeTransmit, + ) + .await + .expect("success"); + assert!(context.response().is_none()); + } + + /// The "finally" interceptors should run upon error when the StopPoint is set to BeforeTransmit + #[tokio::test] + async fn test_stop_points_error_handling() { + #[derive(Debug, Default)] + struct Inner { + modify_before_retry_loop_called: AtomicBool, + modify_before_completion_called: AtomicBool, + read_after_execution_called: AtomicBool, + } + #[derive(Clone, Debug, Default)] + struct TestInterceptor { + inner: Arc, + } + + impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + + fn modify_before_retry_loop( + &self, + _context: &mut BeforeTransmitInterceptorContextMut<'_>, + _rc: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.inner + .modify_before_retry_loop_called + .store(true, Ordering::Relaxed); + Err("test error".into()) + } + + fn modify_before_completion( + &self, + _context: &mut FinalizerInterceptorContextMut<'_>, + _rc: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.inner + .modify_before_completion_called + .store(true, Ordering::Relaxed); + Ok(()) + } + + fn read_after_execution( + &self, + _context: &FinalizerInterceptorContextRef<'_>, + _rc: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.inner + .read_after_execution_called + .store(true, Ordering::Relaxed); + Ok(()) + } + } + + #[derive(Debug)] + struct TestInterceptorRuntimePlugin { + builder: RuntimeComponentsBuilder, + } + impl RuntimePlugin for TestInterceptorRuntimePlugin { + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.builder) + } + } + + let interceptor = TestInterceptor::default(); + let runtime_plugins = || { + RuntimePlugins::new() + .with_operation_plugin(TestOperationRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) + .with_operation_plugin(TestInterceptorRuntimePlugin { + builder: RuntimeComponentsBuilder::new("test") + .with_interceptor(SharedInterceptor::new(interceptor.clone())), + }) + }; + + // StopPoint::BeforeTransmit will exit right before sending the request, so there should be no response + let context = invoke_with_stop_point( + "test", + "test", + Input::doesnt_matter(), + &runtime_plugins(), + StopPoint::BeforeTransmit, + ) + .await + .expect("success"); + assert!(context.response().is_none()); + + assert!(interceptor + .inner + .modify_before_retry_loop_called + .load(Ordering::Relaxed)); + assert!(interceptor + .inner + .modify_before_completion_called + .load(Ordering::Relaxed)); + assert!(interceptor + .inner + .read_after_execution_called + .load(Ordering::Relaxed)); + } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 8f15051a9b..71a9b04873 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -3,34 +3,422 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::phase::Phase; -use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::interceptors::context::Error; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpResponse}; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::auth::{ + AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, AuthSchemeOptionResolver, + AuthSchemeOptionResolverParams, +}; +use aws_smithy_runtime_api::client::identity::IdentityResolver; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::endpoint::Endpoint; +use aws_smithy_types::Document; +use std::borrow::Cow; +use std::error::Error as StdError; +use std::fmt; +use tracing::trace; + +#[derive(Debug)] +enum AuthOrchestrationError { + NoMatchingAuthScheme, + BadAuthSchemeEndpointConfig(Cow<'static, str>), + AuthSchemeEndpointConfigMismatch(String), +} + +impl AuthOrchestrationError { + fn auth_scheme_endpoint_config_mismatch<'a>( + auth_schemes: impl Iterator, + ) -> Self { + Self::AuthSchemeEndpointConfigMismatch( + auth_schemes + .flat_map(|s| match s { + Document::Object(map) => match map.get("name") { + Some(Document::String(name)) => Some(name.as_str()), + _ => None, + }, + _ => None, + }) + .collect::>() + .join(", "), + ) + } +} + +impl fmt::Display for AuthOrchestrationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NoMatchingAuthScheme => f.write_str( + "no auth scheme matched auth scheme options. This is a bug. Please file an issue.", + ), + Self::BadAuthSchemeEndpointConfig(message) => f.write_str(message), + Self::AuthSchemeEndpointConfigMismatch(supported_schemes) => { + write!(f, + "selected auth scheme / endpoint config mismatch. Couldn't find `sigv4` endpoint config for this endpoint. \ + The authentication schemes supported by this endpoint are: {:?}", + supported_schemes + ) + } + } + } +} + +impl StdError for AuthOrchestrationError {} pub(super) async fn orchestrate_auth( - dispatch_phase: Phase, + ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &ConfigBag, -) -> Result> { - dispatch_phase.include_mut(|ctx| { - let params = cfg.auth_option_resolver_params(); - let auth_options = cfg.auth_option_resolver().resolve_auth_options(params)?; - let identity_resolvers = cfg.identity_resolvers(); - - for option in auth_options { - let scheme_id = option.scheme_id(); - if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { - let identity_resolver = auth_scheme.identity_resolver(identity_resolvers); - let request_signer = auth_scheme.request_signer(); - - let identity = identity_resolver.resolve_identity(cfg)?; - let request = ctx.request_mut()?; - request_signer.sign_request(request, &identity, cfg)?; - return Result::<_, BoxError>::Ok(()); +) -> Result<(), BoxError> { + let params = cfg + .load::() + .expect("auth scheme option resolver params must be set"); + let option_resolver = runtime_components.auth_scheme_option_resolver(); + let options = option_resolver.resolve_auth_scheme_options(params)?; + + trace!( + auth_scheme_option_resolver_params = ?params, + auth_scheme_options = ?options, + "orchestrating auth", + ); + + for &scheme_id in options.as_ref() { + if let Some(auth_scheme) = runtime_components.auth_scheme(scheme_id) { + if let Some(identity_resolver) = auth_scheme.identity_resolver(runtime_components) { + let signer = auth_scheme.signer(); + trace!( + auth_scheme = ?auth_scheme, + identity_resolver = ?identity_resolver, + signer = ?signer, + "resolved auth scheme, identity resolver, and signing implementation" + ); + + let endpoint = cfg + .load::() + .expect("endpoint added to config bag by endpoint orchestrator"); + let auth_scheme_endpoint_config = + extract_endpoint_auth_scheme_config(endpoint, scheme_id)?; + trace!(auth_scheme_endpoint_config = ?auth_scheme_endpoint_config, "extracted auth scheme endpoint config"); + + let identity = identity_resolver.resolve_identity(cfg).await?; + trace!(identity = ?identity, "resolved identity"); + + trace!("signing request"); + let request = ctx.request_mut().expect("set during serialization"); + signer.sign_http_request( + request, + &identity, + auth_scheme_endpoint_config, + runtime_components, + cfg, + )?; + return Ok(()); + } + } + } + + Err(AuthOrchestrationError::NoMatchingAuthScheme.into()) +} + +fn extract_endpoint_auth_scheme_config( + endpoint: &Endpoint, + scheme_id: AuthSchemeId, +) -> Result, AuthOrchestrationError> { + let auth_schemes = match endpoint.properties().get("authSchemes") { + Some(Document::Array(schemes)) => schemes, + // no auth schemes: + None => return Ok(AuthSchemeEndpointConfig::from(None)), + _other => { + return Err(AuthOrchestrationError::BadAuthSchemeEndpointConfig( + "expected an array for `authSchemes` in endpoint config".into(), + )) + } + }; + let auth_scheme_config = auth_schemes + .iter() + .find(|doc| { + let config_scheme_id = doc + .as_object() + .and_then(|object| object.get("name")) + .and_then(Document::as_string); + config_scheme_id == Some(scheme_id.as_str()) + }) + .ok_or_else(|| { + AuthOrchestrationError::auth_scheme_endpoint_config_mismatch(auth_schemes.iter()) + })?; + Ok(AuthSchemeEndpointConfig::from(Some(auth_scheme_config))) +} + +#[cfg(all(test, feature = "test-util"))] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolver; + use aws_smithy_runtime_api::client::auth::{ + AuthScheme, AuthSchemeId, AuthSchemeOptionResolverParams, SharedAuthScheme, + SharedAuthSchemeOptionResolver, Signer, + }; + use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolver, SharedIdentityResolver, + }; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; + use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; + use aws_smithy_runtime_api::client::runtime_components::{ + GetIdentityResolver, RuntimeComponentsBuilder, + }; + use aws_smithy_types::config_bag::Layer; + use std::collections::HashMap; + + #[tokio::test] + async fn basic_case() { + #[derive(Debug)] + struct TestIdentityResolver; + impl IdentityResolver for TestIdentityResolver { + fn resolve_identity(&self, _config_bag: &ConfigBag) -> Future { + Future::ready(Ok(Identity::new("doesntmatter", None))) + } + } + + #[derive(Debug)] + struct TestSigner; + + impl Signer for TestSigner { + fn sign_http_request( + &self, + request: &mut HttpRequest, + _identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + request + .headers_mut() + .insert(http::header::AUTHORIZATION, "success!".parse().unwrap()); + Ok(()) } } - Err("No auth scheme matched auth options. This is a bug. Please file an issue.".into()) - }) + const TEST_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("test-scheme"); + + #[derive(Debug)] + struct TestAuthScheme { + signer: TestSigner, + } + impl AuthScheme for TestAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + TEST_SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &dyn GetIdentityResolver, + ) -> Option { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn signer(&self) -> &dyn Signer { + &self.signer + } + } + + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); + + let runtime_components = RuntimeComponentsBuilder::for_tests() + .with_auth_scheme(SharedAuthScheme::new(TestAuthScheme { signer: TestSigner })) + .with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new( + StaticAuthSchemeOptionResolver::new(vec![TEST_SCHEME_ID]), + ))) + .with_identity_resolver( + TEST_SCHEME_ID, + SharedIdentityResolver::new(TestIdentityResolver), + ) + .build() + .unwrap(); + + let mut layer: Layer = Layer::new("test"); + layer.store_put(AuthSchemeOptionResolverParams::new("doesntmatter")); + layer.store_put(Endpoint::builder().url("dontcare").build()); + let cfg = ConfigBag::of_layers(vec![layer]); + + orchestrate_auth(&mut ctx, &runtime_components, &cfg) + .await + .expect("success"); + + assert_eq!( + "success!", + ctx.request() + .expect("request is set") + .headers() + .get("Authorization") + .unwrap() + ); + } + + #[cfg(feature = "http-auth")] + #[tokio::test] + async fn select_best_scheme_for_available_identity_resolvers() { + use crate::client::auth::http::{BasicAuthScheme, BearerAuthScheme}; + use aws_smithy_runtime_api::client::auth::http::{ + HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, + }; + use aws_smithy_runtime_api::client::identity::http::{Login, Token}; + + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); + + fn config_with_identity( + scheme_id: AuthSchemeId, + identity: impl IdentityResolver + 'static, + ) -> (RuntimeComponents, ConfigBag) { + let runtime_components = RuntimeComponentsBuilder::for_tests() + .with_auth_scheme(SharedAuthScheme::new(BasicAuthScheme::new())) + .with_auth_scheme(SharedAuthScheme::new(BearerAuthScheme::new())) + .with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new( + StaticAuthSchemeOptionResolver::new(vec![ + HTTP_BASIC_AUTH_SCHEME_ID, + HTTP_BEARER_AUTH_SCHEME_ID, + ]), + ))) + .with_identity_resolver(scheme_id, SharedIdentityResolver::new(identity)) + .build() + .unwrap(); + + let mut layer = Layer::new("test"); + layer.store_put(Endpoint::builder().url("dontcare").build()); + layer.store_put(AuthSchemeOptionResolverParams::new("doesntmatter")); + + (runtime_components, ConfigBag::of_layers(vec![layer])) + } + + // First, test the presence of a basic auth login and absence of a bearer token + let (runtime_components, cfg) = + config_with_identity(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)); + orchestrate_auth(&mut ctx, &runtime_components, &cfg) + .await + .expect("success"); + assert_eq!( + // "YTpi" == "a:b" in base64 + "Basic YTpi", + ctx.request() + .expect("request is set") + .headers() + .get("Authorization") + .unwrap() + ); + + // Next, test the presence of a bearer token and absence of basic auth + let (runtime_components, cfg) = + config_with_identity(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)); + let mut ctx = InterceptorContext::new(Input::erase("doesnt-matter")); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); + orchestrate_auth(&mut ctx, &runtime_components, &cfg) + .await + .expect("success"); + assert_eq!( + "Bearer t", + ctx.request() + .expect("request is set") + .headers() + .get("Authorization") + .unwrap() + ); + } + + #[test] + fn extract_endpoint_auth_scheme_config_no_config() { + let endpoint = Endpoint::builder() + .url("dontcare") + .property("something-unrelated", Document::Null) + .build(); + let config = extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) + .expect("success"); + assert!(config.as_document().is_none()); + } + + #[test] + fn extract_endpoint_auth_scheme_config_wrong_type() { + let endpoint = Endpoint::builder() + .url("dontcare") + .property("authSchemes", Document::String("bad".into())) + .build(); + extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) + .expect_err("should fail because authSchemes is the wrong type"); + } + + #[test] + fn extract_endpoint_auth_scheme_config_no_matching_scheme() { + let endpoint = Endpoint::builder() + .url("dontcare") + .property( + "authSchemes", + vec![ + Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "wrong-scheme-id".to_string().into()); + out + }), + Document::Object({ + let mut out = HashMap::new(); + out.insert( + "name".to_string(), + "another-wrong-scheme-id".to_string().into(), + ); + out + }), + ], + ) + .build(); + extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) + .expect_err("should fail because authSchemes doesn't include the desired scheme"); + } + + #[test] + fn extract_endpoint_auth_scheme_config_successfully() { + let endpoint = Endpoint::builder() + .url("dontcare") + .property( + "authSchemes", + vec![ + Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "wrong-scheme-id".to_string().into()); + out + }), + Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "test-scheme-id".to_string().into()); + out.insert( + "magicString".to_string(), + "magic string value".to_string().into(), + ); + out + }), + ], + ) + .build(); + let config = extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) + .expect("should find test-scheme-id"); + assert_eq!( + "magic string value", + config + .as_document() + .expect("config is set") + .as_object() + .expect("it's an object") + .get("magicString") + .expect("magicString is set") + .as_string() + .expect("gimme the string, dammit!") + ); + } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs new file mode 100644 index 0000000000..76ce7f4037 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -0,0 +1,171 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::endpoint::error::ResolveEndpointError; +use aws_smithy_http::endpoint::{ + apply_endpoint as apply_endpoint_to_request_uri, EndpointPrefix, ResolveEndpoint, + SharedEndpointResolver, +}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::endpoint::{EndpointResolver, EndpointResolverParams}; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::endpoint::Endpoint; +use http::header::HeaderName; +use http::{HeaderValue, Uri}; +use std::fmt::Debug; +use std::str::FromStr; +use tracing::trace; + +/// An endpoint resolver that uses a static URI. +#[derive(Clone, Debug)] +pub struct StaticUriEndpointResolver { + endpoint: Uri, +} + +impl StaticUriEndpointResolver { + /// Create a resolver that resolves to `http://localhost:{port}`. + pub fn http_localhost(port: u16) -> Self { + Self { + endpoint: Uri::from_str(&format!("http://localhost:{port}")) + .expect("all u16 values are valid ports"), + } + } + + /// Create a resolver that resolves to the given URI. + pub fn uri(endpoint: Uri) -> Self { + Self { endpoint } + } +} + +impl EndpointResolver for StaticUriEndpointResolver { + fn resolve_endpoint(&self, _params: &EndpointResolverParams) -> Future { + Future::ready(Ok(Endpoint::builder() + .url(self.endpoint.to_string()) + .build())) + } +} + +/// Empty params to be used with [`StaticUriEndpointResolver`]. +#[derive(Debug, Default)] +pub struct StaticUriEndpointResolverParams; + +impl StaticUriEndpointResolverParams { + /// Creates a new `StaticUriEndpointResolverParams`. + pub fn new() -> Self { + Self + } +} + +impl From for EndpointResolverParams { + fn from(params: StaticUriEndpointResolverParams) -> Self { + EndpointResolverParams::new(params) + } +} + +/// Default implementation of [`EndpointResolver`]. +/// +/// This default endpoint resolver implements the `EndpointResolver` trait by +/// converting the type-erased [`EndpointResolverParams`] into the concrete +/// endpoint params for the service. It then delegates endpoint resolution +/// to an underlying resolver that is aware of the concrete type. +#[derive(Clone, Debug)] +pub struct DefaultEndpointResolver { + inner: SharedEndpointResolver, +} + +impl Storable for DefaultEndpointResolver +where + Params: Debug + Send + Sync + 'static, +{ + type Storer = StoreReplace; +} + +impl DefaultEndpointResolver { + /// Creates a new `DefaultEndpointResolver`. + pub fn new(resolve_endpoint: SharedEndpointResolver) -> Self { + Self { + inner: resolve_endpoint, + } + } +} + +impl EndpointResolver for DefaultEndpointResolver +where + Params: Debug + Send + Sync + 'static, +{ + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future { + let ep = match params.get::() { + Some(params) => self.inner.resolve_endpoint(params).map_err(Box::new), + None => Err(Box::new(ResolveEndpointError::message( + "params of expected type was not present", + ))), + } + .map_err(|e| e as _); + Future::ready(ep) + } +} + +pub(super) async fn orchestrate_endpoint( + ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, +) -> Result<(), BoxError> { + trace!("orchestrating endpoint resolution"); + + let params = cfg + .load::() + .expect("endpoint resolver params must be set"); + let endpoint_prefix = cfg.load::(); + let request = ctx.request_mut().expect("set during serialization"); + + let endpoint = runtime_components + .endpoint_resolver() + .resolve_endpoint(params) + .await?; + tracing::debug!("will use endpoint {:?}", endpoint); + apply_endpoint(request, &endpoint, endpoint_prefix)?; + + // Make the endpoint config available to interceptors + cfg.interceptor_state().store_put(endpoint); + Ok(()) +} + +fn apply_endpoint( + request: &mut HttpRequest, + endpoint: &Endpoint, + endpoint_prefix: Option<&EndpointPrefix>, +) -> Result<(), BoxError> { + let uri: Uri = endpoint.url().parse().map_err(|err| { + ResolveEndpointError::from_source("endpoint did not have a valid uri", err) + })?; + + apply_endpoint_to_request_uri(request.uri_mut(), &uri, endpoint_prefix).map_err(|err| { + ResolveEndpointError::message(format!( + "failed to apply endpoint `{:?}` to request `{:?}`", + uri, request, + )) + .with_source(Some(err.into())) + })?; + + for (header_name, header_values) in endpoint.headers() { + request.headers_mut().remove(header_name); + for value in header_values { + request.headers_mut().insert( + HeaderName::from_str(header_name).map_err(|err| { + ResolveEndpointError::message("invalid header name") + .with_source(Some(err.into())) + })?, + HeaderValue::from_str(value).map_err(|err| { + ResolveEndpointError::message("invalid header value") + .with_source(Some(err.into())) + })?, + ); + } + } + Ok(()) +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs deleted file mode 100644 index d163b5c743..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_smithy_http::result::{ConnectorError, SdkError}; -use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest, HttpResponse}; - -#[derive(Copy, Clone, Eq, PartialEq)] -enum OrchestrationPhase { - Construction, - Dispatch, - ResponseHandling, -} - -pub(super) struct Phase { - phase: OrchestrationPhase, - context: InterceptorContext, -} - -impl Phase { - pub(crate) fn construction(context: InterceptorContext) -> Self { - Self::start(OrchestrationPhase::Construction, context) - } - pub(crate) fn dispatch(context: InterceptorContext) -> Self { - Self::start(OrchestrationPhase::Dispatch, context) - } - pub(crate) fn response_handling( - context: InterceptorContext, - ) -> Self { - Self::start(OrchestrationPhase::ResponseHandling, context) - } - - fn start( - phase: OrchestrationPhase, - context: InterceptorContext, - ) -> Self { - match phase { - OrchestrationPhase::Construction => {} - OrchestrationPhase::Dispatch => debug_assert!(context.request().is_ok()), - OrchestrationPhase::ResponseHandling => debug_assert!(context.response().is_ok()), - } - Self { phase, context } - } - - pub(crate) fn include_mut>( - mut self, - c: impl FnOnce(&mut InterceptorContext) -> Result<(), E>, - ) -> Result> { - match c(&mut self.context) { - Ok(_) => Ok(self), - Err(e) => Err(self.fail(e)), - } - } - - pub(crate) fn include>( - self, - c: impl FnOnce(&InterceptorContext) -> Result<(), E>, - ) -> Result> { - match c(&self.context) { - Ok(_) => Ok(self), - Err(e) => Err(self.fail(e)), - } - } - - pub(crate) fn fail(self, e: impl Into) -> SdkError { - self.into_sdk_error(e.into()) - } - - pub(crate) fn finalize(self) -> Result> { - debug_assert!(self.phase == OrchestrationPhase::ResponseHandling); - let (_input, output_or_error, _request, response) = self.context.into_parts(); - match output_or_error { - Some(output_or_error) => match output_or_error { - Ok(output) => Ok(output), - Err(error) => Err(SdkError::service_error( - error, - response.expect("response must be set by this point"), - )), - }, - None => unreachable!("phase can't get this far without bubbling up a failure"), - } - } - - fn into_sdk_error(self, e: BoxError) -> SdkError { - let e = match e.downcast::() { - Ok(connector_error) => { - debug_assert!( - self.phase == OrchestrationPhase::Dispatch, - "connector errors should only occur during the dispatch phase" - ); - return SdkError::dispatch_failure(*connector_error); - } - Err(e) => e, - }; - let (_input, output_or_error, _request, response) = self.context.into_parts(); - match self.phase { - OrchestrationPhase::Construction => SdkError::construction_failure(e), - OrchestrationPhase::Dispatch => { - if let Some(response) = response { - SdkError::response_error(e, response) - } else { - SdkError::dispatch_failure(ConnectorError::other(e, None)) - } - } - OrchestrationPhase::ResponseHandling => match (response, output_or_error) { - (Some(response), Some(Err(error))) => SdkError::service_error(error, response), - (Some(response), _) => SdkError::response_error(e, response), - _ => unreachable!("response handling phase at least has a response"), - }, - } - } - - pub(crate) fn finish(self) -> InterceptorContext { - self.context - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries.rs b/rust-runtime/aws-smithy-runtime/src/client/retries.rs new file mode 100644 index 0000000000..8ea71ebb5e --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries.rs @@ -0,0 +1,47 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Smithy retry classifiers. +pub mod classifier; + +/// Smithy retry strategies. +pub mod strategy; + +mod client_rate_limiter; +mod token_bucket; + +use aws_smithy_types::config_bag::{Storable, StoreReplace}; +use std::fmt; + +pub use client_rate_limiter::{ClientRateLimiter, ClientRateLimiterRuntimePlugin}; +pub use token_bucket::{TokenBucket, TokenBucketRuntimePlugin}; + +#[doc(hidden)] +pub use client_rate_limiter::ClientRateLimiterPartition; +#[doc(hidden)] +pub use token_bucket::TokenBucketPartition; + +#[doc(hidden)] +#[non_exhaustive] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct RetryPartition { + inner: &'static str, +} + +impl RetryPartition { + pub fn new(name: &'static str) -> Self { + Self { inner: name } + } +} + +impl fmt::Display for RetryPartition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} + +impl Storable for RetryPartition { + type Storer = StoreReplace; +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs new file mode 100644 index 0000000000..254cb636d5 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs @@ -0,0 +1,263 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; +use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; +use std::borrow::Cow; +use std::error::Error as StdError; +use std::marker::PhantomData; + +/// A retry classifier for checking if an error is modeled as retryable. +#[derive(Debug, Default)] +pub struct ModeledAsRetryableClassifier { + _inner: PhantomData, +} + +impl ModeledAsRetryableClassifier { + /// Create a new `ModeledAsRetryableClassifier` + pub fn new() -> Self { + Self { + _inner: PhantomData, + } + } +} + +impl ClassifyRetry for ModeledAsRetryableClassifier +where + E: StdError + ProvideErrorKind + Send + Sync + 'static, +{ + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + // Check for a result + let output_or_error = ctx.output_or_error()?; + // Check for an error + let error = match output_or_error { + Ok(_) => return None, + Err(err) => err, + }; + // Check that the error is an operation error + let error = error.as_operation_error()?; + // Downcast the error + let error = error.downcast_ref::()?; + // Check if the error is retryable + error.retryable_error_kind().map(RetryReason::Error) + } + + fn name(&self) -> &'static str { + "Errors Modeled As Retryable" + } +} + +/// Classifies response, timeout, and connector errors as retryable or not. +#[derive(Debug, Default)] +pub struct SmithyErrorClassifier { + _inner: PhantomData, +} + +impl SmithyErrorClassifier { + /// Create a new `SmithyErrorClassifier` + pub fn new() -> Self { + Self { + _inner: PhantomData, + } + } +} + +impl ClassifyRetry for SmithyErrorClassifier +where + E: StdError + Send + Sync + 'static, +{ + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + let output_or_error = ctx.output_or_error()?; + // Check for an error + let error = match output_or_error { + Ok(_) => return None, + Err(err) => err, + }; + + if error.is_response_error() || error.is_timeout_error() { + Some(RetryReason::Error(ErrorKind::TransientError)) + } else if let Some(error) = error.as_connector_error() { + if error.is_timeout() || error.is_io() { + Some(RetryReason::Error(ErrorKind::TransientError)) + } else { + error.as_other().map(RetryReason::Error) + } + } else { + None + } + } + + fn name(&self) -> &'static str { + "Retryable Smithy Errors" + } +} + +const TRANSIENT_ERROR_STATUS_CODES: &[u16] = &[500, 502, 503, 504]; + +/// A retry classifier that will treat HTTP response with those status codes as retryable. +/// The `Default` version will retry 500, 502, 503, and 504 errors. +#[derive(Debug)] +pub struct HttpStatusCodeClassifier { + retryable_status_codes: Cow<'static, [u16]>, +} + +impl Default for HttpStatusCodeClassifier { + fn default() -> Self { + Self::new_from_codes(TRANSIENT_ERROR_STATUS_CODES.to_owned()) + } +} + +impl HttpStatusCodeClassifier { + /// Given a `Vec` where the `u16`s represent status codes, create a `HttpStatusCodeClassifier` + /// that will treat HTTP response with those status codes as retryable. The `Default` version + /// will retry 500, 502, 503, and 504 errors. + pub fn new_from_codes(retryable_status_codes: impl Into>) -> Self { + Self { + retryable_status_codes: retryable_status_codes.into(), + } + } +} + +impl ClassifyRetry for HttpStatusCodeClassifier { + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + ctx.response() + .map(|res| res.status().as_u16()) + .map(|status| self.retryable_status_codes.contains(&status)) + .unwrap_or_default() + .then_some(RetryReason::Error(ErrorKind::TransientError)) + } + + fn name(&self) -> &'static str { + "HTTP Status Code" + } +} + +// Generic smithy clients would have something like this: +// pub fn default_retry_classifiers() -> RetryClassifiers { +// RetryClassifiers::new() +// .with_classifier(SmithyErrorClassifier::new()) +// .with_classifier(ModeledAsRetryableClassifier::new()) +// .with_classifier(HttpStatusCodeClassifier::new()) +// } +// This ordering is different than the default AWS ordering because the old generic client classifier +// was the same. + +#[cfg(test)] +mod test { + use crate::client::retries::classifier::{ + HttpStatusCodeClassifier, ModeledAsRetryableClassifier, + }; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, InterceptorContext}; + use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; + use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; + use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; + use std::fmt; + + use super::SmithyErrorClassifier; + + #[derive(Debug, PartialEq, Eq, Clone)] + struct UnmodeledError; + + impl fmt::Display for UnmodeledError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnmodeledError") + } + } + + impl std::error::Error for UnmodeledError {} + + #[test] + fn classify_by_response_status() { + let policy = HttpStatusCodeClassifier::default(); + let res = http::Response::builder() + .status(500) + .body("error!") + .unwrap() + .map(SdkBody::from); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_response(res); + assert_eq!( + policy.classify_retry(&ctx), + Some(RetryReason::Error(ErrorKind::TransientError)) + ); + } + + #[test] + fn classify_by_response_status_not_retryable() { + let policy = HttpStatusCodeClassifier::default(); + let res = http::Response::builder() + .status(408) + .body("error!") + .unwrap() + .map(SdkBody::from); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_response(res); + assert_eq!(policy.classify_retry(&ctx), None); + } + + #[test] + fn classify_by_error_kind() { + #[derive(Debug)] + struct RetryableError; + + impl fmt::Display for RetryableError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Some retryable error") + } + } + + impl ProvideErrorKind for RetryableError { + fn retryable_error_kind(&self) -> Option { + Some(ErrorKind::ClientError) + } + + fn code(&self) -> Option<&str> { + // code should not be called when `error_kind` is provided + unimplemented!() + } + } + + impl std::error::Error for RetryableError {} + + let policy = ModeledAsRetryableClassifier::::new(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase( + RetryableError, + )))); + + assert_eq!( + policy.classify_retry(&ctx), + Some(RetryReason::Error(ErrorKind::ClientError)), + ); + } + + #[test] + fn classify_response_error() { + let policy = SmithyErrorClassifier::::new(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::response( + "I am a response error".into(), + ))); + assert_eq!( + policy.classify_retry(&ctx), + Some(RetryReason::Error(ErrorKind::TransientError)), + ); + } + + #[test] + fn test_timeout_error() { + let policy = SmithyErrorClassifier::::new(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::timeout( + "I am a timeout error".into(), + ))); + assert_eq!( + policy.classify_retry(&ctx), + Some(RetryReason::Error(ErrorKind::TransientError)), + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs new file mode 100644 index 0000000000..661f0f854c --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs @@ -0,0 +1,653 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! A rate limiter for controlling the rate at which AWS requests are made. The rate changes based +//! on the number of throttling errors encountered. + +#![allow(dead_code)] + +use crate::client::retries::RetryPartition; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::{builder, builder_methods, builder_struct}; +use aws_smithy_types::config_bag::{FrozenLayer, Layer, Storable, StoreReplace}; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use tracing::debug; + +/// A [`RuntimePlugin`] to provide a client rate limiter, usable by a retry strategy. +#[non_exhaustive] +#[derive(Debug)] +pub struct ClientRateLimiterRuntimePlugin { + rate_limiter: ClientRateLimiter, +} + +impl ClientRateLimiterRuntimePlugin { + /// Create a new [`ClientRateLimiterRuntimePlugin`]. + pub fn new(seconds_since_unix_epoch: f64) -> Self { + Self { + rate_limiter: ClientRateLimiter::new(seconds_since_unix_epoch), + } + } +} + +impl RuntimePlugin for ClientRateLimiterRuntimePlugin { + fn config(&self) -> Option { + let mut cfg = Layer::new("client rate limiter"); + cfg.store_put(self.rate_limiter.clone()); + + Some(cfg.freeze()) + } +} + +#[doc(hidden)] +#[non_exhaustive] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct ClientRateLimiterPartition { + retry_partition: RetryPartition, +} + +impl ClientRateLimiterPartition { + pub fn new(retry_partition: RetryPartition) -> Self { + Self { retry_partition } + } +} + +const RETRY_COST: f64 = 5.0; +const RETRY_TIMEOUT_COST: f64 = RETRY_COST * 2.0; +const INITIAL_REQUEST_COST: f64 = 1.0; + +const MIN_FILL_RATE: f64 = 0.5; +const MIN_CAPACITY: f64 = 1.0; +const SMOOTH: f64 = 0.8; +/// How much to scale back after receiving a throttling response +const BETA: f64 = 0.7; +/// Controls how aggressively we scale up after being throttled +const SCALE_CONSTANT: f64 = 0.4; + +/// Rate limiter for adaptive retry. +#[derive(Clone, Debug)] +pub struct ClientRateLimiter { + inner: Arc>, +} + +#[derive(Debug)] +pub(crate) struct Inner { + /// The rate at which token are replenished. + fill_rate: f64, + /// The maximum capacity allowed in the token bucket. + max_capacity: f64, + /// The current capacity of the token bucket. + current_capacity: f64, + /// The last time the token bucket was refilled. + last_timestamp: Option, + /// Boolean indicating if the token bucket is enabled. + /// The token bucket is initially disabled. + /// When a throttling error is encountered it is enabled. + enabled: bool, + /// The smoothed rate which tokens are being retrieved. + measured_tx_rate: f64, + /// The last half second time bucket used. + last_tx_rate_bucket: f64, + /// The number of requests seen within the current time bucket. + request_count: u64, + /// The maximum rate when the client was last throttled. + last_max_rate: f64, + /// The last time when the client was throttled. + time_of_last_throttle: f64, +} + +pub(crate) enum RequestReason { + Retry, + RetryTimeout, + InitialRequest, +} + +impl Storable for ClientRateLimiter { + type Storer = StoreReplace; +} + +impl ClientRateLimiter { + /// Creates a new [`ClientRateLimiter`]. + pub fn new(seconds_since_unix_epoch: f64) -> Self { + Self::builder() + .tokens_retrieved_per_second(MIN_FILL_RATE) + .time_of_last_throttle(seconds_since_unix_epoch) + .previous_time_bucket(seconds_since_unix_epoch.floor()) + .build() + } + + fn builder() -> Builder { + Builder::new() + } + + pub(crate) fn acquire_permission_to_send_a_request( + &self, + seconds_since_unix_epoch: f64, + kind: RequestReason, + ) -> Result<(), Duration> { + let mut it = self.inner.lock().unwrap(); + + if !it.enabled { + // return early if we haven't encountered a throttling error yet + return Ok(()); + } + let amount = match kind { + RequestReason::Retry => RETRY_COST, + RequestReason::RetryTimeout => RETRY_TIMEOUT_COST, + RequestReason::InitialRequest => INITIAL_REQUEST_COST, + }; + + it.refill(seconds_since_unix_epoch); + + let res = if amount > it.current_capacity { + let sleep_time = (amount - it.current_capacity) / it.fill_rate; + debug!( + amount, + it.current_capacity, + it.fill_rate, + sleep_time, + "client rate limiter delayed a request" + ); + + Err(Duration::from_secs_f64(sleep_time)) + } else { + Ok(()) + }; + + it.current_capacity -= amount; + res + } + + pub(crate) fn update_rate_limiter( + &self, + seconds_since_unix_epoch: f64, + is_throttling_error: bool, + ) { + let mut it = self.inner.lock().unwrap(); + it.update_tokens_retrieved_per_second(seconds_since_unix_epoch); + + let calculated_rate; + if is_throttling_error { + let rate_to_use = if it.enabled { + f64::min(it.measured_tx_rate, it.fill_rate) + } else { + it.measured_tx_rate + }; + + // The fill_rate is from the token bucket + it.last_max_rate = rate_to_use; + it.calculate_time_window(); + it.time_of_last_throttle = seconds_since_unix_epoch; + calculated_rate = cubic_throttle(rate_to_use); + it.enable_token_bucket(); + } else { + it.calculate_time_window(); + calculated_rate = it.cubic_success(seconds_since_unix_epoch); + } + + let new_rate = f64::min(calculated_rate, 2.0 * it.measured_tx_rate); + it.update_bucket_refill_rate(seconds_since_unix_epoch, new_rate); + } +} + +impl Inner { + fn refill(&mut self, seconds_since_unix_epoch: f64) { + if let Some(last_timestamp) = self.last_timestamp { + let fill_amount = (seconds_since_unix_epoch - last_timestamp) * self.fill_rate; + self.current_capacity = + f64::min(self.max_capacity, self.current_capacity + fill_amount); + debug!( + fill_amount, + self.current_capacity, self.max_capacity, "refilling client rate limiter tokens" + ); + } + self.last_timestamp = Some(seconds_since_unix_epoch); + } + + fn update_bucket_refill_rate(&mut self, seconds_since_unix_epoch: f64, new_fill_rate: f64) { + // Refill based on our current rate before we update to the new fill rate. + self.refill(seconds_since_unix_epoch); + + self.fill_rate = f64::max(new_fill_rate, MIN_FILL_RATE); + self.max_capacity = f64::max(new_fill_rate, MIN_CAPACITY); + + debug!( + fill_rate = self.fill_rate, + max_capacity = self.max_capacity, + current_capacity = self.current_capacity, + measured_tx_rate = self.measured_tx_rate, + "client rate limiter state has been updated" + ); + + // When we scale down we can't have a current capacity that exceeds our max_capacity. + self.current_capacity = f64::min(self.current_capacity, self.max_capacity); + } + + fn enable_token_bucket(&mut self) { + // If throttling wasn't already enabled, note that we're now enabling it. + if !self.enabled { + debug!("client rate limiting has been enabled"); + } + self.enabled = true; + } + + fn update_tokens_retrieved_per_second(&mut self, seconds_since_unix_epoch: f64) { + let next_time_bucket = (seconds_since_unix_epoch * 2.0).floor() / 2.0; + self.request_count += 1; + + if next_time_bucket > self.last_tx_rate_bucket { + let current_rate = + self.request_count as f64 / (next_time_bucket - self.last_tx_rate_bucket); + self.measured_tx_rate = current_rate * SMOOTH + self.measured_tx_rate * (1.0 - SMOOTH); + self.request_count = 0; + self.last_tx_rate_bucket = next_time_bucket; + } + } + + fn calculate_time_window(&self) -> f64 { + let base = (self.last_max_rate * (1.0 - BETA)) / SCALE_CONSTANT; + base.powf(1.0 / 3.0) + } + + fn cubic_success(&self, seconds_since_unix_epoch: f64) -> f64 { + let dt = + seconds_since_unix_epoch - self.time_of_last_throttle - self.calculate_time_window(); + (SCALE_CONSTANT * dt.powi(3)) + self.last_max_rate + } +} + +fn cubic_throttle(rate_to_use: f64) -> f64 { + rate_to_use * BETA +} + +builder!( + set_token_refill_rate, token_refill_rate, f64, "The rate at which token are replenished.", + set_maximum_bucket_capacity, maximum_bucket_capacity, f64, "The maximum capacity allowed in the token bucket.", + set_current_bucket_capacity, current_bucket_capacity, f64, "The current capacity of the token bucket. The minimum this can be is 1.0", + set_time_of_last_refill, time_of_last_refill, f64, "The last time the token bucket was refilled.", + set_tokens_retrieved_per_second, tokens_retrieved_per_second, f64, "The smoothed rate which tokens are being retrieved.", + set_previous_time_bucket, previous_time_bucket, f64, "The last half second time bucket used.", + set_request_count, request_count, u64, "The number of requests seen within the current time bucket.", + set_enable_throttling, enable_throttling, bool, "Boolean indicating if the token bucket is enabled. The token bucket is initially disabled. When a throttling error is encountered it is enabled.", + set_tokens_retrieved_per_second_at_time_of_last_throttle, tokens_retrieved_per_second_at_time_of_last_throttle, f64, "The maximum rate when the client was last throttled.", + set_time_of_last_throttle, time_of_last_throttle, f64, "The last time when the client was throttled." +); + +impl Builder { + fn build(self) -> ClientRateLimiter { + ClientRateLimiter { + inner: Arc::new(Mutex::new(Inner { + fill_rate: self.token_refill_rate.unwrap_or_default(), + max_capacity: self.maximum_bucket_capacity.unwrap_or(f64::MAX), + current_capacity: self.current_bucket_capacity.unwrap_or_default(), + last_timestamp: self.time_of_last_refill, + enabled: self.enable_throttling.unwrap_or_default(), + measured_tx_rate: self.tokens_retrieved_per_second.unwrap_or_default(), + last_tx_rate_bucket: self.previous_time_bucket.unwrap_or_default(), + request_count: self.request_count.unwrap_or_default(), + last_max_rate: self + .tokens_retrieved_per_second_at_time_of_last_throttle + .unwrap_or_default(), + time_of_last_throttle: self.time_of_last_throttle.unwrap_or_default(), + })), + } + } +} + +#[cfg(test)] +mod tests { + use super::{cubic_throttle, ClientRateLimiter}; + use crate::client::retries::client_rate_limiter::RequestReason; + use approx::assert_relative_eq; + use aws_smithy_async::rt::sleep::AsyncSleep; + use aws_smithy_async::test_util::instant_time_and_sleep; + use std::time::{Duration, SystemTime}; + + const ONE_SECOND: Duration = Duration::from_secs(1); + const TWO_HUNDRED_MILLISECONDS: Duration = Duration::from_millis(200); + + #[test] + fn should_match_beta_decrease() { + let new_rate = cubic_throttle(10.0); + assert_relative_eq!(new_rate, 7.0); + + let rate_limiter = ClientRateLimiter::builder() + .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) + .time_of_last_throttle(1.0) + .build(); + + rate_limiter.inner.lock().unwrap().calculate_time_window(); + let new_rate = rate_limiter.inner.lock().unwrap().cubic_success(1.0); + assert_relative_eq!(new_rate, 7.0); + } + + #[tokio::test] + async fn throttling_is_enabled_once_throttling_error_is_received() { + let rate_limiter = ClientRateLimiter::builder() + .previous_time_bucket(0.0) + .time_of_last_throttle(0.0) + .build(); + + assert!( + !rate_limiter.inner.lock().unwrap().enabled, + "rate_limiter should be disabled by default" + ); + rate_limiter.update_rate_limiter(0.0, true); + assert!( + rate_limiter.inner.lock().unwrap().enabled, + "rate_limiter should be enabled after throttling error" + ); + } + + #[tokio::test] + async fn test_calculated_rate_with_successes() { + let rate_limiter = ClientRateLimiter::builder() + .time_of_last_throttle(5.0) + .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) + .build(); + + struct Attempt { + seconds_since_unix_epoch: f64, + expected_calculated_rate: f64, + } + + let attempts = [ + Attempt { + seconds_since_unix_epoch: 5.0, + expected_calculated_rate: 7.0, + }, + Attempt { + seconds_since_unix_epoch: 6.0, + expected_calculated_rate: 9.64893600966, + }, + Attempt { + seconds_since_unix_epoch: 7.0, + expected_calculated_rate: 10.000030849917364, + }, + Attempt { + seconds_since_unix_epoch: 8.0, + expected_calculated_rate: 10.453284520772092, + }, + Attempt { + seconds_since_unix_epoch: 9.0, + expected_calculated_rate: 13.408697022224185, + }, + Attempt { + seconds_since_unix_epoch: 10.0, + expected_calculated_rate: 21.26626835427364, + }, + Attempt { + seconds_since_unix_epoch: 11.0, + expected_calculated_rate: 36.425998516920465, + }, + ]; + + // Think this test is a little strange? I ported the test from Go v2, and this is how it + // was implemented. See for yourself: + // https://github.com/aws/aws-sdk-go-v2/blob/844ff45cdc76182229ad098c95bf3f5ab8c20e9f/aws/retry/adaptive_ratelimit_test.go#L97 + for attempt in attempts { + rate_limiter.inner.lock().unwrap().calculate_time_window(); + let calculated_rate = rate_limiter + .inner + .lock() + .unwrap() + .cubic_success(attempt.seconds_since_unix_epoch); + + assert_relative_eq!(attempt.expected_calculated_rate, calculated_rate); + } + } + + #[tokio::test] + async fn test_calculated_rate_with_throttles() { + let rate_limiter = ClientRateLimiter::builder() + .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) + .time_of_last_throttle(5.0) + .build(); + + struct Attempt { + throttled: bool, + seconds_since_unix_epoch: f64, + expected_calculated_rate: f64, + } + + let attempts = [ + Attempt { + throttled: false, + seconds_since_unix_epoch: 5.0, + expected_calculated_rate: 7.0, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 6.0, + expected_calculated_rate: 9.64893600966, + }, + Attempt { + throttled: true, + seconds_since_unix_epoch: 7.0, + expected_calculated_rate: 6.754255206761999, + }, + Attempt { + throttled: true, + seconds_since_unix_epoch: 8.0, + expected_calculated_rate: 4.727978644733399, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 9.0, + expected_calculated_rate: 4.670125557970046, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 10.0, + expected_calculated_rate: 4.770870456867401, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 11.0, + expected_calculated_rate: 6.011819748005445, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 12.0, + expected_calculated_rate: 10.792973431384178, + }, + ]; + + // Think this test is a little strange? I ported the test from Go v2, and this is how it + // was implemented. See for yourself: + // https://github.com/aws/aws-sdk-go-v2/blob/844ff45cdc76182229ad098c95bf3f5ab8c20e9f/aws/retry/adaptive_ratelimit_test.go#L97 + let mut calculated_rate = 0.0; + for attempt in attempts { + let mut inner = rate_limiter.inner.lock().unwrap(); + inner.calculate_time_window(); + if attempt.throttled { + calculated_rate = cubic_throttle(calculated_rate); + inner.time_of_last_throttle = attempt.seconds_since_unix_epoch; + inner.last_max_rate = calculated_rate; + } else { + calculated_rate = inner.cubic_success(attempt.seconds_since_unix_epoch); + }; + + assert_relative_eq!(attempt.expected_calculated_rate, calculated_rate); + } + } + + #[tokio::test] + async fn test_client_sending_rates() { + let (_, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + let rate_limiter = ClientRateLimiter::builder().build(); + + struct Attempt { + throttled: bool, + seconds_since_unix_epoch: f64, + expected_tokens_retrieved_per_second: f64, + expected_token_refill_rate: f64, + } + + let attempts = [ + Attempt { + throttled: false, + seconds_since_unix_epoch: 0.2, + expected_tokens_retrieved_per_second: 0.000000, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 0.4, + expected_tokens_retrieved_per_second: 0.000000, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 0.6, + expected_tokens_retrieved_per_second: 4.800000000000001, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 0.8, + expected_tokens_retrieved_per_second: 4.800000000000001, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.0, + expected_tokens_retrieved_per_second: 4.160000, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.2, + expected_tokens_retrieved_per_second: 4.160000, + expected_token_refill_rate: 0.691200, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.4, + expected_tokens_retrieved_per_second: 4.160000, + expected_token_refill_rate: 1.0975999999999997, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.6, + expected_tokens_retrieved_per_second: 5.632000000000001, + expected_token_refill_rate: 1.6384000000000005, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.8, + expected_tokens_retrieved_per_second: 5.632000000000001, + expected_token_refill_rate: 2.332800, + }, + Attempt { + throttled: true, + seconds_since_unix_epoch: 2.0, + expected_tokens_retrieved_per_second: 4.326400, + expected_token_refill_rate: 3.0284799999999996, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 2.2, + expected_tokens_retrieved_per_second: 4.326400, + expected_token_refill_rate: 3.48663917347026, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 2.4, + expected_tokens_retrieved_per_second: 4.326400, + expected_token_refill_rate: 3.821874416040255, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 2.6, + expected_tokens_retrieved_per_second: 5.665280, + expected_token_refill_rate: 4.053385727709987, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 2.8, + expected_tokens_retrieved_per_second: 5.665280, + expected_token_refill_rate: 4.200373108479454, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 3.0, + expected_tokens_retrieved_per_second: 4.333056, + expected_token_refill_rate: 4.282036558348658, + }, + Attempt { + throttled: true, + seconds_since_unix_epoch: 3.2, + expected_tokens_retrieved_per_second: 4.333056, + expected_token_refill_rate: 2.99742559084406, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 3.4, + expected_tokens_retrieved_per_second: 4.333056, + expected_token_refill_rate: 3.4522263943863463, + }, + ]; + + for attempt in attempts { + sleep_impl.sleep(TWO_HUNDRED_MILLISECONDS).await; + assert_eq!( + attempt.seconds_since_unix_epoch, + sleep_impl.total_duration().as_secs_f64() + ); + + rate_limiter.update_rate_limiter(attempt.seconds_since_unix_epoch, attempt.throttled); + assert_relative_eq!( + attempt.expected_tokens_retrieved_per_second, + rate_limiter.inner.lock().unwrap().measured_tx_rate + ); + assert_relative_eq!( + attempt.expected_token_refill_rate, + rate_limiter.inner.lock().unwrap().fill_rate + ); + } + } + + // This test is only testing that we don't fail basic math and panic. It does include an + // element of randomness, but no duration between >= 0.0s and <= 1.0s will ever cause a panic. + // + // Because the cost of sending an individual request is 1.0, and because the minimum capacity is + // also 1.0, we will never encounter a situation where we run out of tokens. + #[tokio::test] + async fn test_when_throttling_is_enabled_requests_can_still_be_sent() { + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + let crl = ClientRateLimiter::builder() + .time_of_last_throttle(0.0) + .previous_time_bucket(0.0) + .build(); + + // Start by recording a throttling error + crl.update_rate_limiter(0.0, true); + + for _i in 0..100 { + // advance time by a random amount (up to 1s) each iteration + let duration = Duration::from_secs_f64(fastrand::f64()); + sleep_impl.sleep(duration).await; + if let Err(delay) = crl.acquire_permission_to_send_a_request( + time_source.seconds_since_unix_epoch(), + RequestReason::InitialRequest, + ) { + sleep_impl.sleep(delay).await; + } + + // Assume all further requests succeed on the first try + crl.update_rate_limiter(time_source.seconds_since_unix_epoch(), false); + } + + let inner = crl.inner.lock().unwrap(); + assert!(inner.enabled, "the rate limiter should still be enabled"); + // Assert that the rate limiter respects the passage of time. + assert_relative_eq!( + inner.last_timestamp.unwrap(), + sleep_impl.total_duration().as_secs_f64(), + max_relative = 0.0001 + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs new file mode 100644 index 0000000000..c441aa4816 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs @@ -0,0 +1,15 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +mod never; +pub(crate) mod standard; + +pub use never::NeverRetryStrategy; +pub use standard::StandardRetryStrategy; + +#[cfg(feature = "test-util")] +mod fixed_delay; +#[cfg(feature = "test-util")] +pub use fixed_delay::FixedDelayRetryStrategy; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs new file mode 100644 index 0000000000..886b7a61b2 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -0,0 +1,105 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::retries::{ + ClassifyRetry, RequestAttempts, RetryReason, RetryStrategy, ShouldAttempt, +}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use std::time::Duration; + +/// A retry policy used in tests. This relies on an error classifier already present in the config bag. +/// If a server response is retryable, it will be retried after a fixed delay. +#[derive(Debug, Clone)] +pub struct FixedDelayRetryStrategy { + fixed_delay: Duration, + max_attempts: u32, +} + +impl FixedDelayRetryStrategy { + /// Create a new retry policy with a fixed delay. + pub fn new(fixed_delay: Duration) -> Self { + Self { + fixed_delay, + max_attempts: 4, + } + } + + /// Create a new retry policy with a one second delay. + pub fn one_second_delay() -> Self { + Self::new(Duration::from_secs(1)) + } +} + +impl RetryStrategy for FixedDelayRetryStrategy { + fn should_attempt_initial_request( + &self, + _runtime_components: &RuntimeComponents, + _cfg: &ConfigBag, + ) -> Result { + Ok(ShouldAttempt::Yes) + } + + fn should_attempt_retry( + &self, + ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result { + // Look a the result. If it's OK then we're done; No retry required. Otherwise, we need to inspect it + let output_or_error = ctx.output_or_error().expect( + "This must never be called without reaching the point where the result exists.", + ); + if output_or_error.is_ok() { + tracing::trace!("request succeeded, no retry necessary"); + return Ok(ShouldAttempt::No); + } + + // Check if we're out of attempts + let request_attempts = cfg + .load::() + .expect("at least one request attempt is made before any retry is attempted"); + if request_attempts.attempts() >= self.max_attempts { + tracing::trace!( + attempts = request_attempts.attempts(), + max_attempts = self.max_attempts, + "not retrying because we are out of attempts" + ); + return Ok(ShouldAttempt::No); + } + + let retry_classifiers = runtime_components + .retry_classifiers() + .expect("a retry classifier is set"); + let retry_reason = retry_classifiers.classify_retry(ctx); + + let backoff = match retry_reason { + Some(RetryReason::Explicit(_)) => self.fixed_delay, + Some(RetryReason::Error(_)) => self.fixed_delay, + Some(_) => { + unreachable!("RetryReason is non-exhaustive. Therefore, we need to cover this unreachable case.") + } + None => { + tracing::trace!( + attempts = request_attempts.attempts(), + max_attempts = self.max_attempts, + "encountered unretryable error" + ); + return Ok(ShouldAttempt::No); + } + }; + + tracing::debug!( + "attempt {} failed with {:?}; retrying after {:?}", + request_attempts.attempts(), + retry_reason, + backoff + ); + + Ok(ShouldAttempt::YesAfterDelay(backoff)) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs new file mode 100644 index 0000000000..7baf4a1426 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; + +/// A retry strategy that never retries. +#[non_exhaustive] +#[derive(Debug, Clone, Default)] +pub struct NeverRetryStrategy; + +impl NeverRetryStrategy { + /// Creates a new `NeverRetryStrategy`. + pub fn new() -> Self { + Self::default() + } +} + +impl RetryStrategy for NeverRetryStrategy { + fn should_attempt_initial_request( + &self, + _runtime_components: &RuntimeComponents, + _cfg: &ConfigBag, + ) -> Result { + Ok(ShouldAttempt::Yes) + } + + fn should_attempt_retry( + &self, + _context: &InterceptorContext, + _runtime_components: &RuntimeComponents, + _cfg: &ConfigBag, + ) -> Result { + Ok(ShouldAttempt::No) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs new file mode 100644 index 0000000000..ee32a6a63d --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -0,0 +1,764 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::retries::client_rate_limiter::{ClientRateLimiter, RequestReason}; +use crate::client::retries::strategy::standard::ReleaseResult::{ + APermitWasReleased, NoPermitWasReleased, +}; +use crate::client::retries::token_bucket::TokenBucket; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::retries::{ + ClassifyRetry, RequestAttempts, RetryReason, RetryStrategy, ShouldAttempt, +}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::retry::{ErrorKind, RetryConfig}; +use std::sync::Mutex; +use std::time::{Duration, SystemTime}; +use tokio::sync::OwnedSemaphorePermit; +use tracing::debug; + +// The initial attempt, plus three retries. +const DEFAULT_MAX_ATTEMPTS: u32 = 4; + +/// Retry strategy with exponential backoff, max attempts, and a token bucket. +#[derive(Debug)] +pub struct StandardRetryStrategy { + // Retry settings + base: fn() -> f64, + initial_backoff: Duration, + max_attempts: u32, + max_backoff: Duration, + retry_permit: Mutex>, +} + +impl Storable for StandardRetryStrategy { + type Storer = StoreReplace; +} + +impl StandardRetryStrategy { + /// Create a new standard retry strategy with the given config. + pub fn new(retry_config: &RetryConfig) -> Self { + let base = if retry_config.use_static_exponential_base() { + || 1.0 + } else { + fastrand::f64 + }; + Self::default() + .with_base(base) + .with_max_backoff(retry_config.max_backoff()) + .with_max_attempts(retry_config.max_attempts()) + .with_initial_backoff(retry_config.initial_backoff()) + } + + /// Changes the exponential backoff base. + pub fn with_base(mut self, base: fn() -> f64) -> Self { + self.base = base; + self + } + + /// Changes the max number of attempts. + pub fn with_max_attempts(mut self, max_attempts: u32) -> Self { + self.max_attempts = max_attempts; + self + } + + /// Changes the initial backoff time. + pub fn with_initial_backoff(mut self, initial_backoff: Duration) -> Self { + self.initial_backoff = initial_backoff; + self + } + + /// Changes the maximum backoff time. + pub fn with_max_backoff(mut self, max_backoff: Duration) -> Self { + self.max_backoff = max_backoff; + self + } + + fn release_retry_permit(&self) -> ReleaseResult { + let mut retry_permit = self.retry_permit.lock().unwrap(); + match retry_permit.take() { + Some(p) => { + drop(p); + APermitWasReleased + } + None => NoPermitWasReleased, + } + } + + fn set_retry_permit(&self, new_retry_permit: OwnedSemaphorePermit) { + let mut old_retry_permit = self.retry_permit.lock().unwrap(); + if let Some(p) = old_retry_permit.replace(new_retry_permit) { + // Whenever we set a new retry permit and it replaces the old one, we need to "forget" + // the old permit, removing it from the bucket forever. + p.forget() + } + } + + fn calculate_backoff( + &self, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + retry_reason: Option<&RetryReason>, + ) -> Result { + let request_attempts = cfg + .load::() + .expect("at least one request attempt is made before any retry is attempted") + .attempts(); + let token_bucket = cfg.load::(); + + match retry_reason { + Some(RetryReason::Explicit(backoff)) => Ok(*backoff), + Some(RetryReason::Error(kind)) => { + update_rate_limiter_if_exists( + runtime_components, + cfg, + *kind == ErrorKind::ThrottlingError, + ); + if let Some(delay) = check_rate_limiter_for_delay(runtime_components, cfg, *kind) { + let delay = delay.min(self.max_backoff); + debug!("rate limiter has requested a {delay:?} delay before retrying"); + Ok(delay) + } else { + if let Some(tb) = token_bucket { + match tb.acquire(kind) { + Some(permit) => self.set_retry_permit(permit), + None => { + debug!("attempt #{request_attempts} failed with {kind:?}; However, no retry permits are available, so no retry will be attempted."); + return Err(ShouldAttempt::No); + } + } + } + + let backoff = calculate_exponential_backoff( + // Generate a random base multiplier to create jitter + (self.base)(), + // Get the backoff time multiplier in seconds (with fractional seconds) + self.initial_backoff.as_secs_f64(), + // `self.local.attempts` tracks number of requests made including the initial request + // The initial attempt shouldn't count towards backoff calculations so we subtract it + request_attempts - 1, + ); + Ok(Duration::from_secs_f64(backoff).min(self.max_backoff)) + } + } + Some(_) => unreachable!("RetryReason is non-exhaustive"), + None => { + update_rate_limiter_if_exists(runtime_components, cfg, false); + debug!( + attempts = request_attempts, + max_attempts = self.max_attempts, + "encountered unretryable error" + ); + Err(ShouldAttempt::No) + } + } + } +} + +enum ReleaseResult { + APermitWasReleased, + NoPermitWasReleased, +} + +impl Default for StandardRetryStrategy { + fn default() -> Self { + Self { + max_attempts: DEFAULT_MAX_ATTEMPTS, + max_backoff: Duration::from_secs(20), + // by default, use a random base for exponential backoff + base: fastrand::f64, + initial_backoff: Duration::from_secs(1), + retry_permit: Mutex::new(None), + } + } +} + +impl RetryStrategy for StandardRetryStrategy { + fn should_attempt_initial_request( + &self, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result { + if let Some(crl) = cfg.load::() { + let seconds_since_unix_epoch = get_seconds_since_unix_epoch(runtime_components); + if let Err(delay) = crl.acquire_permission_to_send_a_request( + seconds_since_unix_epoch, + RequestReason::InitialRequest, + ) { + return Ok(ShouldAttempt::YesAfterDelay(delay)); + } + } else { + debug!("no client rate limiter configured, so no token is required for the initial request."); + } + + Ok(ShouldAttempt::Yes) + } + + fn should_attempt_retry( + &self, + ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result { + // Look a the result. If it's OK then we're done; No retry required. Otherwise, we need to inspect it + let output_or_error = ctx.output_or_error().expect( + "This must never be called without reaching the point where the result exists.", + ); + let token_bucket = cfg.load::(); + if output_or_error.is_ok() { + debug!("request succeeded, no retry necessary"); + if let Some(tb) = token_bucket { + // If this retry strategy is holding any permits, release them back to the bucket. + if let NoPermitWasReleased = self.release_retry_permit() { + // In the event that there was no retry permit to release, we generate new + // permits from nothing. We do this to make up for permits we had to "forget". + // Otherwise, repeated retries would empty the bucket and nothing could fill it + // back up again. + tb.regenerate_a_token(); + } + } + update_rate_limiter_if_exists(runtime_components, cfg, false); + + return Ok(ShouldAttempt::No); + } + + // Check if we're out of attempts + let request_attempts = cfg + .load::() + .expect("at least one request attempt is made before any retry is attempted") + .attempts(); + if request_attempts >= self.max_attempts { + update_rate_limiter_if_exists(runtime_components, cfg, false); + + debug!( + attempts = request_attempts, + max_attempts = self.max_attempts, + "not retrying because we are out of attempts" + ); + return Ok(ShouldAttempt::No); + } + + // Run the classifiers against the context to determine if we should retry + let retry_classifiers = runtime_components + .retry_classifiers() + .ok_or("retry classifiers are required by the retry configuration")?; + let retry_reason = retry_classifiers.classify_retry(ctx); + + // Calculate the appropriate backoff time. + let backoff = match self.calculate_backoff(runtime_components, cfg, retry_reason.as_ref()) { + Ok(value) => value, + // In some cases, backoff calculation will decide that we shouldn't retry at all. + Err(value) => return Ok(value), + }; + debug!( + "attempt #{request_attempts} failed with {:?}; retrying after {:?}", + retry_reason.expect("the match statement above ensures this is not None"), + backoff, + ); + + Ok(ShouldAttempt::YesAfterDelay(backoff)) + } +} + +fn update_rate_limiter_if_exists( + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + is_throttling_error: bool, +) { + if let Some(crl) = cfg.load::() { + let seconds_since_unix_epoch = get_seconds_since_unix_epoch(runtime_components); + crl.update_rate_limiter(seconds_since_unix_epoch, is_throttling_error); + } +} + +fn check_rate_limiter_for_delay( + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + kind: ErrorKind, +) -> Option { + if let Some(crl) = cfg.load::() { + let retry_reason = if kind == ErrorKind::ThrottlingError { + RequestReason::RetryTimeout + } else { + RequestReason::Retry + }; + if let Err(delay) = crl.acquire_permission_to_send_a_request( + get_seconds_since_unix_epoch(runtime_components), + retry_reason, + ) { + return Some(delay); + } + } + + None +} + +fn calculate_exponential_backoff(base: f64, initial_backoff: f64, retry_attempts: u32) -> f64 { + base * initial_backoff * 2_u32.pow(retry_attempts) as f64 +} + +fn get_seconds_since_unix_epoch(runtime_components: &RuntimeComponents) -> f64 { + let request_time = runtime_components + .time_source() + .expect("time source required for retries"); + request_time + .now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs_f64() +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; + use aws_smithy_runtime_api::client::retries::{ + AlwaysRetry, ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, + }; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + use aws_smithy_types::config_bag::Layer; + use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; + use std::fmt; + use std::sync::Mutex; + use std::time::Duration; + + #[cfg(feature = "test-util")] + use crate::client::retries::token_bucket::TokenBucket; + use aws_smithy_runtime_api::client::interceptors::context::{Input, Output}; + + #[test] + fn no_retry_necessary_for_ok_result() { + let cfg = ConfigBag::base(); + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + let strategy = StandardRetryStrategy::default(); + ctx.set_output_or_error(Ok(Output::doesnt_matter())); + let actual = strategy + .should_attempt_retry(&ctx, &rc, &cfg) + .expect("method is infallible for this use"); + assert_eq!(ShouldAttempt::No, actual); + } + + fn set_up_cfg_and_context( + error_kind: ErrorKind, + current_request_attempts: u32, + ) -> (InterceptorContext, RuntimeComponents, ConfigBag) { + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); + let rc = RuntimeComponentsBuilder::for_tests() + .with_retry_classifiers(Some( + RetryClassifiers::new().with_classifier(AlwaysRetry(error_kind)), + )) + .build() + .unwrap(); + let mut layer = Layer::new("test"); + layer.store_put(RequestAttempts::new(current_request_attempts)); + let cfg = ConfigBag::of_layers(vec![layer]); + + (ctx, rc, cfg) + } + + // Test that error kinds produce the correct "retry after X seconds" output. + // All error kinds are handled in the same way for the standard strategy. + fn test_should_retry_error_kind(error_kind: ErrorKind) { + let (ctx, rc, cfg) = set_up_cfg_and_context(error_kind, 3); + let strategy = StandardRetryStrategy::default().with_base(|| 1.0); + let actual = strategy + .should_attempt_retry(&ctx, &rc, &cfg) + .expect("method is infallible for this use"); + assert_eq!(ShouldAttempt::YesAfterDelay(Duration::from_secs(4)), actual); + } + + #[test] + fn should_retry_transient_error_result_after_2s() { + test_should_retry_error_kind(ErrorKind::TransientError); + } + + #[test] + fn should_retry_client_error_result_after_2s() { + test_should_retry_error_kind(ErrorKind::ClientError); + } + + #[test] + fn should_retry_server_error_result_after_2s() { + test_should_retry_error_kind(ErrorKind::ServerError); + } + + #[test] + fn should_retry_throttling_error_result_after_2s() { + test_should_retry_error_kind(ErrorKind::ThrottlingError); + } + + #[test] + fn dont_retry_when_out_of_attempts() { + let current_attempts = 4; + let max_attempts = current_attempts; + let (ctx, rc, cfg) = set_up_cfg_and_context(ErrorKind::TransientError, current_attempts); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(max_attempts); + let actual = strategy + .should_attempt_retry(&ctx, &rc, &cfg) + .expect("method is infallible for this use"); + assert_eq!(ShouldAttempt::No, actual); + } + + #[derive(Debug)] + struct ServerError; + impl fmt::Display for ServerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OperationError") + } + } + + impl std::error::Error for ServerError {} + + impl ProvideErrorKind for ServerError { + fn retryable_error_kind(&self) -> Option { + Some(ErrorKind::ServerError) + } + + fn code(&self) -> Option<&str> { + None + } + } + + #[derive(Debug)] + struct PresetReasonRetryClassifier { + retry_reasons: Mutex>, + } + + #[cfg(feature = "test-util")] + impl PresetReasonRetryClassifier { + fn new(mut retry_reasons: Vec) -> Self { + // We'll pop the retry_reasons in reverse order so we reverse the list to fix that. + retry_reasons.reverse(); + Self { + retry_reasons: Mutex::new(retry_reasons), + } + } + } + + impl ClassifyRetry for PresetReasonRetryClassifier { + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + if ctx.output_or_error().map(|it| it.is_ok()).unwrap_or(false) { + return None; + } + + let mut retry_reasons = self.retry_reasons.lock().unwrap(); + if retry_reasons.len() == 1 { + Some(retry_reasons.first().unwrap().clone()) + } else { + retry_reasons.pop() + } + } + + fn name(&self) -> &'static str { + "Always returns a preset retry reason" + } + } + + #[cfg(feature = "test-util")] + fn setup_test( + retry_reasons: Vec, + ) -> (ConfigBag, RuntimeComponents, InterceptorContext) { + let rc = RuntimeComponentsBuilder::for_tests() + .with_retry_classifiers(Some( + RetryClassifiers::new() + .with_classifier(PresetReasonRetryClassifier::new(retry_reasons)), + )) + .build() + .unwrap(); + let cfg = ConfigBag::base(); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + // This type doesn't matter b/c the classifier will just return whatever we tell it to. + ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); + + (cfg, rc, ctx) + } + + #[cfg(feature = "test-util")] + #[test] + fn eventual_success() { + let (mut cfg, rc, mut ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5); + cfg.interceptor_state().store_put(TokenBucket::default()); + let token_bucket = cfg.load::().unwrap().clone(); + + cfg.interceptor_state().store_put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 495); + + cfg.interceptor_state().store_put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(2)); + assert_eq!(token_bucket.available_permits(), 490); + + ctx.set_output_or_error(Ok(Output::doesnt_matter())); + + cfg.interceptor_state().store_put(RequestAttempts::new(3)); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 495); + } + + #[cfg(feature = "test-util")] + #[test] + fn no_more_attempts() { + let (mut cfg, rc, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(3); + cfg.interceptor_state().store_put(TokenBucket::default()); + let token_bucket = cfg.load::().unwrap().clone(); + + cfg.interceptor_state().store_put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 495); + + cfg.interceptor_state().store_put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(2)); + assert_eq!(token_bucket.available_permits(), 490); + + cfg.interceptor_state().store_put(RequestAttempts::new(3)); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 490); + } + + #[cfg(feature = "test-util")] + #[test] + fn no_quota() { + let (mut cfg, rc, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5); + cfg.interceptor_state().store_put(TokenBucket::new(5)); + let token_bucket = cfg.load::().unwrap().clone(); + + cfg.interceptor_state().store_put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 0); + + cfg.interceptor_state().store_put(RequestAttempts::new(2)); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 0); + } + + #[cfg(feature = "test-util")] + #[test] + fn quota_replenishes_on_success() { + let (mut cfg, rc, mut ctx) = setup_test(vec![ + RetryReason::Error(ErrorKind::TransientError), + RetryReason::Explicit(Duration::from_secs(1)), + ]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5); + cfg.interceptor_state().store_put(TokenBucket::new(100)); + let token_bucket = cfg.load::().unwrap().clone(); + + cfg.interceptor_state().store_put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 90); + + cfg.interceptor_state().store_put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 90); + + ctx.set_output_or_error(Ok(Output::doesnt_matter())); + + cfg.interceptor_state().store_put(RequestAttempts::new(3)); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + + assert_eq!(token_bucket.available_permits(), 100); + } + + #[cfg(feature = "test-util")] + #[test] + fn quota_replenishes_on_first_try_success() { + const PERMIT_COUNT: usize = 20; + let (mut cfg, rc, mut ctx) = + setup_test(vec![RetryReason::Error(ErrorKind::TransientError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(u32::MAX); + cfg.interceptor_state() + .store_put(TokenBucket::new(PERMIT_COUNT)); + let token_bucket = cfg.load::().unwrap().clone(); + + let mut attempt = 1; + + // Drain all available permits with failed attempts + while token_bucket.available_permits() > 0 { + // Draining should complete in 2 attempts + if attempt > 2 { + panic!("This test should have completed by now (drain)"); + } + + cfg.interceptor_state() + .store_put(RequestAttempts::new(attempt)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + assert!(matches!(should_retry, ShouldAttempt::YesAfterDelay(_))); + attempt += 1; + } + + // Forget the permit so that we can only refill by "success on first try". + let permit = strategy.retry_permit.lock().unwrap().take().unwrap(); + permit.forget(); + + ctx.set_output_or_error(Ok(Output::doesnt_matter())); + + // Replenish permits until we get back to `PERMIT_COUNT` + while token_bucket.available_permits() < PERMIT_COUNT { + if attempt > 23 { + panic!("This test should have completed by now (fill-up)"); + } + + cfg.interceptor_state() + .store_put(RequestAttempts::new(attempt)); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + attempt += 1; + } + + assert_eq!(attempt, 23); + assert_eq!(token_bucket.available_permits(), PERMIT_COUNT); + } + + #[cfg(feature = "test-util")] + #[test] + fn backoff_timing() { + let (mut cfg, rc, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5); + cfg.interceptor_state().store_put(TokenBucket::default()); + let token_bucket = cfg.load::().unwrap().clone(); + + cfg.interceptor_state().store_put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 495); + + cfg.interceptor_state().store_put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(2)); + assert_eq!(token_bucket.available_permits(), 490); + + cfg.interceptor_state().store_put(RequestAttempts::new(3)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(4)); + assert_eq!(token_bucket.available_permits(), 485); + + cfg.interceptor_state().store_put(RequestAttempts::new(4)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(8)); + assert_eq!(token_bucket.available_permits(), 480); + + cfg.interceptor_state().store_put(RequestAttempts::new(5)); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 480); + } + + #[cfg(feature = "test-util")] + #[test] + fn max_backoff_time() { + let (mut cfg, rc, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5) + .with_initial_backoff(Duration::from_secs(1)) + .with_max_backoff(Duration::from_secs(3)); + cfg.interceptor_state().store_put(TokenBucket::default()); + let token_bucket = cfg.load::().unwrap().clone(); + + cfg.interceptor_state().store_put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 495); + + cfg.interceptor_state().store_put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(2)); + assert_eq!(token_bucket.available_permits(), 490); + + cfg.interceptor_state().store_put(RequestAttempts::new(3)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(3)); + assert_eq!(token_bucket.available_permits(), 485); + + cfg.interceptor_state().store_put(RequestAttempts::new(4)); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(3)); + assert_eq!(token_bucket.available_permits(), 480); + + cfg.interceptor_state().store_put(RequestAttempts::new(5)); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 480); + } + + #[test] + fn calculate_exponential_backoff_where_initial_backoff_is_one() { + let initial_backoff = 1.0; + + for (attempt, expected_backoff) in [initial_backoff, 2.0, 4.0].into_iter().enumerate() { + let actual_backoff = + calculate_exponential_backoff(1.0, initial_backoff, attempt as u32); + assert_eq!(expected_backoff, actual_backoff); + } + } + + #[test] + fn calculate_exponential_backoff_where_initial_backoff_is_greater_than_one() { + let initial_backoff = 3.0; + + for (attempt, expected_backoff) in [initial_backoff, 6.0, 12.0].into_iter().enumerate() { + let actual_backoff = + calculate_exponential_backoff(1.0, initial_backoff, attempt as u32); + assert_eq!(expected_backoff, actual_backoff); + } + } + + #[test] + fn calculate_exponential_backoff_where_initial_backoff_is_less_than_one() { + let initial_backoff = 0.03; + + for (attempt, expected_backoff) in [initial_backoff, 0.06, 0.12].into_iter().enumerate() { + let actual_backoff = + calculate_exponential_backoff(1.0, initial_backoff, attempt as u32); + assert_eq!(expected_backoff, actual_backoff); + } + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs new file mode 100644 index 0000000000..686185b1d1 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs @@ -0,0 +1,116 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::retries::RetryPartition; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::{FrozenLayer, Layer, Storable, StoreReplace}; +use aws_smithy_types::retry::ErrorKind; +use std::sync::Arc; +use tokio::sync::{OwnedSemaphorePermit, Semaphore}; +use tracing::trace; + +/// A [`RuntimePlugin`] to provide a token bucket, usable by a retry strategy. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct TokenBucketRuntimePlugin { + token_bucket: TokenBucket, +} + +impl TokenBucketRuntimePlugin { + /// Creates a new `TokenBucketRuntimePlugin` with the given initial quota. + pub fn new(initial_tokens: usize) -> Self { + Self { + token_bucket: TokenBucket::new(initial_tokens), + } + } +} + +impl RuntimePlugin for TokenBucketRuntimePlugin { + fn config(&self) -> Option { + let mut cfg = Layer::new("standard token bucket"); + cfg.store_put(self.token_bucket.clone()); + + Some(cfg.freeze()) + } +} + +#[doc(hidden)] +#[non_exhaustive] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct TokenBucketPartition { + retry_partition: RetryPartition, +} + +impl TokenBucketPartition { + pub fn new(retry_partition: RetryPartition) -> Self { + Self { retry_partition } + } +} + +const DEFAULT_CAPACITY: usize = 500; +const RETRY_COST: u32 = 5; +const RETRY_TIMEOUT_COST: u32 = RETRY_COST * 2; +const PERMIT_REGENERATION_AMOUNT: usize = 1; + +/// Token bucket used for standard and adaptive retry. +#[derive(Clone, Debug)] +pub struct TokenBucket { + semaphore: Arc, + max_permits: usize, + timeout_retry_cost: u32, + retry_cost: u32, +} + +impl Storable for TokenBucket { + type Storer = StoreReplace; +} + +impl Default for TokenBucket { + fn default() -> Self { + Self { + semaphore: Arc::new(Semaphore::new(DEFAULT_CAPACITY)), + max_permits: DEFAULT_CAPACITY, + timeout_retry_cost: RETRY_TIMEOUT_COST, + retry_cost: RETRY_COST, + } + } +} + +impl TokenBucket { + /// Creates a new `TokenBucket` with the given initial quota. + pub fn new(initial_quota: usize) -> Self { + Self { + semaphore: Arc::new(Semaphore::new(initial_quota)), + max_permits: initial_quota, + retry_cost: RETRY_COST, + timeout_retry_cost: RETRY_TIMEOUT_COST, + } + } + + pub(crate) fn acquire(&self, err: &ErrorKind) -> Option { + let retry_cost = if err == &ErrorKind::TransientError { + self.timeout_retry_cost + } else { + self.retry_cost + }; + + self.semaphore + .clone() + .try_acquire_many_owned(retry_cost) + .ok() + } + + pub(crate) fn regenerate_a_token(&self) { + if self.semaphore.available_permits() < (self.max_permits) { + trace!("adding {PERMIT_REGENERATION_AMOUNT} back into the bucket"); + self.semaphore.add_permits(PERMIT_REGENERATION_AMOUNT) + } + } + + #[cfg(all(test, feature = "test-util"))] + pub(crate) fn available_permits(&self) -> usize { + self.semaphore.available_permits() + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs new file mode 100644 index 0000000000..1173adc3db --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs @@ -0,0 +1,10 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Test response deserializer implementations. +pub mod deserializer; + +/// Test request serializer implementations. +pub mod serializer; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs new file mode 100644 index 0000000000..4e83052d43 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; +use aws_smithy_runtime_api::client::orchestrator::{HttpResponse, OrchestratorError}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::client::ser_de::{ResponseDeserializer, SharedResponseDeserializer}; +use aws_smithy_types::config_bag::{FrozenLayer, Layer}; +use std::sync::Mutex; + +/// Test response deserializer that always returns the same canned response. +#[derive(Default, Debug)] +pub struct CannedResponseDeserializer { + inner: Mutex>>>, +} + +impl CannedResponseDeserializer { + /// Creates a new `CannedResponseDeserializer` with the given canned response. + pub fn new(output: Result>) -> Self { + Self { + inner: Mutex::new(Some(output)), + } + } + + fn take(&self) -> Option>> { + match self.inner.lock() { + Ok(mut guard) => guard.take(), + Err(_) => None, + } + } +} + +impl ResponseDeserializer for CannedResponseDeserializer { + fn deserialize_nonstreaming( + &self, + _response: &HttpResponse, + ) -> Result> { + self.take() + .ok_or("CannedResponseDeserializer's inner value has already been taken.") + .unwrap() + } +} + +impl RuntimePlugin for CannedResponseDeserializer { + fn config(&self) -> Option { + let mut cfg = Layer::new("CannedResponse"); + cfg.store_put(SharedResponseDeserializer::new(Self { + inner: Mutex::new(self.take()), + })); + Some(cfg.freeze()) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs new file mode 100644 index 0000000000..573eea5293 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -0,0 +1,62 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::Input; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::client::ser_de::{RequestSerializer, SharedRequestSerializer}; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; +use std::sync::Mutex; + +/// Test [`RequestSerializer`] that returns a canned request. +#[derive(Default, Debug)] +pub struct CannedRequestSerializer { + inner: Mutex>>, +} + +impl CannedRequestSerializer { + /// Create a new [`CannedRequestSerializer`] with a successful canned request. + pub fn success(request: HttpRequest) -> Self { + Self { + inner: Mutex::new(Some(Ok(request))), + } + } + + /// Create a new [`CannedRequestSerializer`] with a canned error. + pub fn failure(error: BoxError) -> Self { + Self { + inner: Mutex::new(Some(Err(error))), + } + } + + fn take(&self) -> Option> { + match self.inner.lock() { + Ok(mut guard) => guard.take(), + Err(_) => None, + } + } +} + +impl RequestSerializer for CannedRequestSerializer { + fn serialize_input( + &self, + _input: Input, + _cfg: &mut ConfigBag, + ) -> Result { + self.take() + .ok_or("CannedRequestSerializer's inner value has already been taken.")? + } +} + +impl RuntimePlugin for CannedRequestSerializer { + fn config(&self) -> Option { + let mut cfg = Layer::new("CannedRequest"); + cfg.store_put(SharedRequestSerializer::new(Self { + inner: Mutex::new(self.take()), + })); + Some(cfg.freeze()) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs new file mode 100644 index 0000000000..c149878dda --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -0,0 +1,244 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_async::future::timeout::Timeout; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; +use aws_smithy_client::SdkError; +use aws_smithy_runtime_api::client::orchestrator::HttpResponse; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::timeout::TimeoutConfig; +use pin_project_lite::pin_project; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::time::Duration; + +#[derive(Debug)] +struct MaybeTimeoutError { + kind: TimeoutKind, + duration: Duration, +} + +impl MaybeTimeoutError { + fn new(kind: TimeoutKind, duration: Duration) -> Self { + Self { kind, duration } + } +} + +impl std::fmt::Display for MaybeTimeoutError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} occurred after {:?}", + match self.kind { + TimeoutKind::Operation => "operation timeout (all attempts including retries)", + TimeoutKind::OperationAttempt => "operation attempt timeout (single attempt)", + }, + self.duration + ) + } +} + +impl std::error::Error for MaybeTimeoutError {} + +pin_project! { + #[non_exhaustive] + #[must_use = "futures do nothing unless you `.await` or poll them"] + // This allow is needed because otherwise Clippy will get mad we didn't document the + // generated MaybeTimeoutFutureProj + #[allow(missing_docs)] + #[project = MaybeTimeoutFutureProj] + /// A timeout future that may or may not have a timeout depending on + /// whether or not one was set. A `kind` can be set so that when a timeout occurs, there + /// is additional context attached to the error. + pub(super) enum MaybeTimeoutFuture { + /// A wrapper around an inner future that will output an [`SdkError`] if it runs longer than + /// the given duration + Timeout { + #[pin] + future: Timeout, + timeout_kind: TimeoutKind, + duration: Duration, + }, + /// A thin wrapper around an inner future that will never time out + NoTimeout { + #[pin] + future: F + } + } +} + +impl Future for MaybeTimeoutFuture +where + InnerFuture: Future>>, +{ + type Output = Result>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let (future, kind, duration) = match self.project() { + MaybeTimeoutFutureProj::NoTimeout { future } => return future.poll(cx), + MaybeTimeoutFutureProj::Timeout { + future, + timeout_kind, + duration, + } => (future, timeout_kind, duration), + }; + match future.poll(cx) { + Poll::Ready(Ok(response)) => Poll::Ready(response), + Poll::Ready(Err(_timeout)) => Poll::Ready(Err(SdkError::timeout_error( + MaybeTimeoutError::new(*kind, *duration), + ))), + Poll::Pending => Poll::Pending, + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(super) enum TimeoutKind { + Operation, + OperationAttempt, +} + +#[derive(Clone, Debug)] +pub(super) struct MaybeTimeoutConfig { + sleep_impl: Option, + timeout: Option, + timeout_kind: TimeoutKind, +} + +impl MaybeTimeoutConfig { + pub(super) fn new( + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + timeout_kind: TimeoutKind, + ) -> MaybeTimeoutConfig { + if let Some(timeout_config) = cfg.load::() { + let sleep_impl = runtime_components.sleep_impl(); + let timeout = match (sleep_impl.as_ref(), timeout_kind) { + (None, _) => None, + (Some(_), TimeoutKind::Operation) => timeout_config.operation_timeout(), + (Some(_), TimeoutKind::OperationAttempt) => { + timeout_config.operation_attempt_timeout() + } + }; + MaybeTimeoutConfig { + sleep_impl, + timeout, + timeout_kind, + } + } else { + MaybeTimeoutConfig { + sleep_impl: None, + timeout: None, + timeout_kind, + } + } + } +} + +/// Trait to conveniently wrap a future with an optional timeout. +pub(super) trait MaybeTimeout: Sized { + /// Wraps a future in a timeout if one is set. + fn maybe_timeout(self, timeout_config: MaybeTimeoutConfig) -> MaybeTimeoutFuture; +} + +impl MaybeTimeout for T +where + T: Future, +{ + fn maybe_timeout(self, timeout_config: MaybeTimeoutConfig) -> MaybeTimeoutFuture { + match timeout_config { + MaybeTimeoutConfig { + sleep_impl: Some(sleep_impl), + timeout: Some(timeout), + timeout_kind, + } => MaybeTimeoutFuture::Timeout { + future: Timeout::new(self, sleep_impl.sleep(timeout)), + timeout_kind, + duration: timeout, + }, + _ => MaybeTimeoutFuture::NoTimeout { future: self }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_async::assert_elapsed; + use aws_smithy_async::future::never::Never; + use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, TokioSleep}; + use aws_smithy_http::result::SdkError; + use aws_smithy_runtime_api::client::orchestrator::HttpResponse; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + use aws_smithy_types::config_bag::{CloneableLayer, ConfigBag}; + use aws_smithy_types::timeout::TimeoutConfig; + use std::time::Duration; + + #[tokio::test] + async fn test_no_timeout() { + let sleep_impl = SharedAsyncSleep::new(TokioSleep::new()); + let sleep_future = sleep_impl.sleep(Duration::from_millis(250)); + let underlying_future = async { + sleep_future.await; + Result::<_, SdkError<(), HttpResponse>>::Ok(()) + }; + + let now = tokio::time::Instant::now(); + tokio::time::pause(); + + let runtime_components = RuntimeComponentsBuilder::for_tests() + .with_sleep_impl(Some(sleep_impl)) + .build() + .unwrap(); + + let mut timeout_config = CloneableLayer::new("timeout"); + timeout_config.store_put(TimeoutConfig::builder().build()); + let cfg = ConfigBag::of_layers(vec![timeout_config.into()]); + + let maybe_timeout = + MaybeTimeoutConfig::new(&runtime_components, &cfg, TimeoutKind::Operation); + underlying_future + .maybe_timeout(maybe_timeout) + .await + .expect("success"); + + assert_elapsed!(now, Duration::from_secs_f32(0.25)); + } + + #[tokio::test] + async fn test_operation_timeout() { + let sleep_impl = SharedAsyncSleep::new(TokioSleep::new()); + let never = Never::new(); + let underlying_future = async { + never.await; + Result::<_, SdkError<(), HttpResponse>>::Ok(()) + }; + + let now = tokio::time::Instant::now(); + tokio::time::pause(); + + let runtime_components = RuntimeComponentsBuilder::for_tests() + .with_sleep_impl(Some(sleep_impl)) + .build() + .unwrap(); + let mut timeout_config = CloneableLayer::new("timeout"); + timeout_config.store_put( + TimeoutConfig::builder() + .operation_timeout(Duration::from_millis(250)) + .build(), + ); + let cfg = ConfigBag::of_layers(vec![timeout_config.into()]); + + let maybe_timeout = + MaybeTimeoutConfig::new(&runtime_components, &cfg, TimeoutKind::Operation); + let result = underlying_future.maybe_timeout(maybe_timeout).await; + let err = result.expect_err("should have timed out"); + + assert_eq!(format!("{:?}", err), "TimeoutError(TimeoutError { source: MaybeTimeoutError { kind: Operation, duration: 250ms } })"); + assert_elapsed!(now, Duration::from_secs_f32(0.25)); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/lib.rs b/rust-runtime/aws-smithy-runtime/src/lib.rs index 195fde2d06..b40610a77c 100644 --- a/rust-runtime/aws-smithy-runtime/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime/src/lib.rs @@ -3,11 +3,28 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Runtime support logic and types for smithy-rs generated code. +//! +//! # Crate Features +//! +//! - `http-auth`: Enables auth scheme and identity resolver implementations for HTTP API Key, +//! Basic Auth, Bearer Token, and Digest Auth. +//! - `test-util`: Enables utilities for unit tests. DO NOT ENABLE IN PRODUCTION. + #![warn( - // missing_docs, - // rustdoc::missing_crate_level_docs, + missing_docs, + rustdoc::missing_crate_level_docs, unreachable_pub, rust_2018_idioms )] +/// Runtime support logic for generated clients. +#[cfg(feature = "client")] pub mod client; + +/// A data structure for persisting and sharing state between multiple clients. +pub mod static_partition_map; + +/// General testing utilities. +#[cfg(feature = "test-util")] +pub mod test_util; diff --git a/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs b/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs new file mode 100644 index 0000000000..2f70869f35 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs @@ -0,0 +1,162 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use once_cell::sync::OnceCell; +use std::collections::HashMap; +use std::hash::Hash; +use std::sync::{Mutex, MutexGuard}; + +/// A data structure for persisting and sharing state between multiple clients. +/// +/// Some state should be shared between multiple clients. For example, when creating multiple clients +/// for the same service, it's desirable to share a client rate limiter. This way, when one client +/// receives a throttling response, the other clients will be aware of it as well. +/// +/// Whether clients share state is dependent on their partition key `K`. Going back to the client +/// rate limiter example, `K` would be a struct containing the name of the service as well as the +/// client's configured region, since receiving throttling responses in `us-east-1` shouldn't +/// throttle requests to the same service made in other regions. +/// +/// Values stored in a `StaticPartitionMap` will be cloned whenever they are requested. Values must +/// be initialized before they can be retrieved, and the `StaticPartitionMap::get_or_init` method is +/// how you can ensure this. +/// +/// # Example +/// +/// ``` +///use std::sync::{Arc, Mutex}; +/// use aws_smithy_runtime::static_partition_map::StaticPartitionMap; +/// +/// // The shared state must be `Clone` and will be internally mutable. Deriving `Default` isn't +/// // necessary, but allows us to use the `StaticPartitionMap::get_or_init_default` method. +/// #[derive(Clone, Default)] +/// pub struct SomeSharedState { +/// inner: Arc> +/// } +/// +/// #[derive(Default)] +/// struct Inner { +/// // Some shared state... +/// } +/// +/// // `Clone`, `Hash`, and `Eq` are all required trait impls for partition keys +/// #[derive(Clone, Hash, PartialEq, Eq)] +/// pub struct SharedStatePartition { +/// region: String, +/// service_name: String, +/// } +/// +/// impl SharedStatePartition { +/// pub fn new(region: impl Into, service_name: impl Into) -> Self { +/// Self { region: region.into(), service_name: service_name.into() } +/// } +/// } +/// +/// static SOME_SHARED_STATE: StaticPartitionMap = StaticPartitionMap::new(); +/// +/// struct Client { +/// shared_state: SomeSharedState, +/// } +/// +/// impl Client { +/// pub fn new() -> Self { +/// let key = SharedStatePartition::new("us-east-1", "example_service_20230628"); +/// Self { +/// // If the stored value implements `Default`, you can call the +/// // `StaticPartitionMap::get_or_init_default` convenience method. +/// shared_state: SOME_SHARED_STATE.get_or_init_default(key), +/// } +/// } +/// } +/// ``` +#[derive(Debug, Default)] +pub struct StaticPartitionMap { + inner: OnceCell>>, +} + +impl StaticPartitionMap { + /// Creates a new `StaticPartitionMap`. + pub const fn new() -> Self { + Self { + inner: OnceCell::new(), + } + } +} + +impl StaticPartitionMap +where + K: Eq + Hash, +{ + fn get_or_init_inner(&self) -> MutexGuard<'_, HashMap> { + self.inner + // At the very least, we'll always be storing the default state. + .get_or_init(|| Mutex::new(HashMap::with_capacity(1))) + .lock() + .unwrap() + } +} + +impl StaticPartitionMap +where + K: Eq + Hash, + V: Clone, +{ + /// Gets the value for the given partition key. + #[must_use] + pub fn get(&self, partition_key: K) -> Option { + self.get_or_init_inner().get(&partition_key).cloned() + } + + /// Gets the value for the given partition key, initializing it with `init` if it doesn't exist. + #[must_use] + pub fn get_or_init(&self, partition_key: K, init: F) -> V + where + F: FnOnce() -> V, + { + let mut inner = self.get_or_init_inner(); + let v = inner.entry(partition_key).or_insert_with(init); + v.clone() + } +} + +impl StaticPartitionMap +where + K: Eq + Hash, + V: Clone + Default, +{ + /// Gets the value for the given partition key, initializing it if it doesn't exist. + #[must_use] + pub fn get_or_init_default(&self, partition_key: K) -> V { + self.get_or_init(partition_key, V::default) + } +} + +#[cfg(test)] +mod tests { + use super::StaticPartitionMap; + + #[test] + fn test_keyed_partition_returns_same_value_for_same_key() { + let kp = StaticPartitionMap::new(); + let _ = kp.get_or_init("A", || "A".to_owned()); + let actual = kp.get_or_init("A", || "B".to_owned()); + let expected = "A".to_owned(); + assert_eq!(expected, actual); + } + + #[test] + fn test_keyed_partition_returns_different_value_for_different_key() { + let kp = StaticPartitionMap::new(); + let _ = kp.get_or_init("A", || "A".to_owned()); + let actual = kp.get_or_init("B", || "B".to_owned()); + + let expected = "B".to_owned(); + assert_eq!(expected, actual); + + let actual = kp.get("A").unwrap(); + let expected = "A".to_owned(); + assert_eq!(expected, actual); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/test_util.rs b/rust-runtime/aws-smithy-runtime/src/test_util.rs new file mode 100644 index 0000000000..b3ec5e5dfb --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/test_util.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Utility for capturing and displaying logs during a unit test. +pub mod capture_test_logs; diff --git a/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs b/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs new file mode 100644 index 0000000000..85730d00bc --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs @@ -0,0 +1,95 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::env; +use std::io::Write; +use std::sync::{Arc, Mutex}; +use tracing::subscriber::DefaultGuard; +use tracing::Level; +use tracing_subscriber::fmt::TestWriter; + +/// A guard that resets log capturing upon being dropped. +#[derive(Debug)] +pub struct LogCaptureGuard(DefaultGuard); + +/// Capture logs from this test. +/// +/// The logs will be captured until the `DefaultGuard` is dropped. +/// +/// *Why use this instead of traced_test?* +/// This captures _all_ logs, not just logs produced by the current crate. +#[must_use] // log capturing ceases the instant the `DefaultGuard` is dropped +pub fn capture_test_logs() -> (LogCaptureGuard, Rx) { + // it may be helpful to upstream this at some point + let (mut writer, rx) = Tee::stdout(); + if env::var("VERBOSE_TEST_LOGS").is_ok() { + eprintln!("Enabled verbose test logging."); + writer.loud(); + } else { + eprintln!("To see full logs from this test set VERBOSE_TEST_LOGS=true"); + } + let subscriber = tracing_subscriber::fmt() + .with_max_level(Level::TRACE) + .with_writer(Mutex::new(writer)) + .finish(); + let guard = tracing::subscriber::set_default(subscriber); + (LogCaptureGuard(guard), rx) +} + +/// Receiver for the captured logs. +pub struct Rx(Arc>>); +impl Rx { + /// Returns the captured logs as a string. + /// + /// # Panics + /// This will panic if the logs are not valid UTF-8. + pub fn contents(&self) -> String { + String::from_utf8(self.0.lock().unwrap().clone()).unwrap() + } +} + +struct Tee { + buf: Arc>>, + quiet: bool, + inner: W, +} + +impl Tee { + fn stdout() -> (Self, Rx) { + let buf: Arc>> = Default::default(); + ( + Tee { + buf: buf.clone(), + quiet: true, + inner: TestWriter::new(), + }, + Rx(buf), + ) + } +} + +impl Tee { + fn loud(&mut self) { + self.quiet = false; + } +} + +impl Write for Tee +where + W: Write, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buf.lock().unwrap().extend_from_slice(buf); + if !self.quiet { + self.inner.write(buf) + } else { + Ok(buf.len()) + } + } + + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} diff --git a/rust-runtime/aws-smithy-types-convert/Cargo.toml b/rust-runtime/aws-smithy-types-convert/Cargo.toml index c9ffbd22a0..f657571a48 100644 --- a/rust-runtime/aws-smithy-types-convert/Cargo.toml +++ b/rust-runtime/aws-smithy-types-convert/Cargo.toml @@ -13,7 +13,7 @@ convert-time = ["aws-smithy-types", "time"] [dependencies] aws-smithy-types = { path = "../aws-smithy-types", optional = true } -chrono = { version = "0.4.19", optional = true, default-features = false, features = ["std"] } +chrono = { version = "0.4.23", optional = true, default-features = false, features = ["std"] } time = { version = "0.3.4", optional = true } [package.metadata.docs.rs] diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 7ebd3ece23..ba4d59bb82 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -1,27 +1,36 @@ [package] name = "aws-smithy-types" version = "0.0.0-smithy-rs-head" -authors = ["AWS Rust SDK Team ", "Russell Cohen "] +authors = [ + "AWS Rust SDK Team ", + "Russell Cohen ", +] description = "Types for smithy-rs codegen." edition = "2021" license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" +[features] +test-util = [] +serde-serialize = [] +serde-deserialize = [] + [dependencies] +base64-simd = "0.8" itoa = "1.0.0" num-integer = "0.1.44" ryu = "1.0.5" time = { version = "0.3.4", features = ["parsing"] } -base64-simd = "0.8" [dev-dependencies] base64 = "0.13.0" +ciborium = { version = "0.2.1" } +criterion = "0.4" lazy_static = "1.4" proptest = "1" +rand = "0.8.4" serde = { version = "1", features = ["derive"] } serde_json = "1" -criterion = "0.4" -rand = "0.8.4" [package.metadata.docs.rs] all-features = true @@ -32,3 +41,7 @@ rustdoc-args = ["--cfg", "docsrs"] [[bench]] name = "base64" harness = false + +[target."cfg(aws_sdk_unstable)".dependencies.serde] +version = "1" +features = ["derive"] diff --git a/rust-runtime/aws-smithy-types/additional-ci b/rust-runtime/aws-smithy-types/additional-ci index c1fd7ce406..2d9305d510 100755 --- a/rust-runtime/aws-smithy-types/additional-ci +++ b/rust-runtime/aws-smithy-types/additional-ci @@ -5,8 +5,10 @@ # # This script contains additional CI checks to run for this specific package - set -e echo "### Checking for duplicate dependency versions in the normal dependency graph with all features enabled" cargo tree -d --edges normal --all-features + +echo "### Checking whether the features are properly feature-gated" +! cargo tree -e no-dev | grep serde diff --git a/rust-runtime/aws-smithy-types/src/blob.rs b/rust-runtime/aws-smithy-types/src/blob.rs new file mode 100644 index 0000000000..5365b91249 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/blob.rs @@ -0,0 +1,157 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Binary Blob Type +/// +/// Blobs represent protocol-agnostic binary content. +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct Blob { + inner: Vec, +} + +impl Blob { + /// Creates a new blob from the given `input`. + pub fn new>>(input: T) -> Self { + Blob { + inner: input.into(), + } + } + + /// Consumes the `Blob` and returns a `Vec` with its contents. + pub fn into_inner(self) -> Vec { + self.inner + } +} + +impl AsRef<[u8]> for Blob { + fn as_ref(&self) -> &[u8] { + &self.inner + } +} + +#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))] +mod serde_serialize { + use super::*; + use crate::base64; + use serde::Serialize; + + impl Serialize for Blob { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if serializer.is_human_readable() { + serializer.serialize_str(&crate::base64::encode(&self.inner)) + } else { + serializer.serialize_bytes(&self.inner) + } + } + } +} + +#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))] +mod serde_deserialize { + use super::*; + use crate::base64; + use serde::{de::Visitor, Deserialize}; + + struct HumanReadableBlobVisitor; + impl<'de> Visitor<'de> for HumanReadableBlobVisitor { + type Value = Blob; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("expected base64 encoded string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match base64::decode(v) { + Ok(inner) => Ok(Blob { inner }), + Err(e) => Err(E::custom(e)), + } + } + } + + struct NotHumanReadableBlobVisitor; + impl<'de> Visitor<'de> for NotHumanReadableBlobVisitor { + type Value = Blob; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("expected bytes") + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: serde::de::Error, + { + Ok(Blob { inner: v }) + } + } + + impl<'de> Deserialize<'de> for Blob { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + deserializer.deserialize_str(HumanReadableBlobVisitor) + } else { + deserializer.deserialize_byte_buf(NotHumanReadableBlobVisitor) + } + } + } +} + +#[cfg(test)] +#[cfg(all( + aws_sdk_unstable, + feature = "serde-serialize", + feature = "serde-deserialize" +))] +mod test_serde { + use crate::Blob; + use serde::{Deserialize, Serialize}; + use std::collections::HashMap; + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct ForTest { + blob: Blob, + } + + #[test] + fn human_readable_blob() { + let aws_in_base64 = r#"{"blob":"QVdT"}"#; + let for_test = ForTest { + blob: Blob { + inner: vec![b'A', b'W', b'S'], + }, + }; + assert_eq!(for_test, serde_json::from_str(aws_in_base64).unwrap()); + assert_eq!(serde_json::to_string(&for_test).unwrap(), aws_in_base64); + } + + #[test] + fn not_human_readable_blob() { + use std::ffi::CString; + + let for_test = ForTest { + blob: Blob { + inner: vec![b'A', b'W', b'S'], + }, + }; + let mut buf = vec![]; + let res = ciborium::ser::into_writer(&for_test, &mut buf); + assert!(res.is_ok()); + + // checks whether the bytes are deserialized properly + let n: HashMap = + ciborium::de::from_reader(std::io::Cursor::new(buf.clone())).unwrap(); + assert!(n.get("blob").is_some()); + assert!(n.get("blob") == CString::new([65, 87, 83]).ok().as_ref()); + + let de: ForTest = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap(); + assert_eq!(for_test, de); + } +} diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs new file mode 100644 index 0000000000..f2bcc01d4c --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -0,0 +1,1028 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Layers and layered bags of configuration data. +//! +//! The [`ConfigBag`](crate::config_bag::ConfigBag) structure is used to store and pass around configuration for client operations. +//! Interacting with it may be required in order to write an `Interceptor` or `RuntimePlugin` to +//! customize a client. +//! +//! A `ConfigBag` is essentially a stack of several immutable and sharable layers, with a single _mutable_ layer at +//! the top of the stack that is called "interceptor state". The intent of this last mutable layer is to allow for +//! more performant mutation of config within the execution of an operation. +//! +//! There are three separate layer types to be aware of when using a `ConfigBag`: +//! 1. [`Layer`](crate::config_bag::Layer) - A mutable layer. This is usually only used for adding config +//! to the `ConfigBag`, but is also used for the interceptor state. +//! 2. [`CloneableLayer`](crate::config_bag::CloneableLayer) - Identical to `Layer`, except that it requires +//! `Clone` bounds on the items added to it so that it can be deep cloned. Can be converted to a `Layer` +//! while retaining the cloneability of its items such that the resulting layer could be cloned as long as +//! nothing else is added to it later. A `Layer` cannot be converted back into a `CloneableLayer`. +//! 3. [`FrozenLayer`](crate::config_bag::FrozenLayer) - Basically an [`Arc`](std::sync::Arc) wrapper around +//! a `Layer`. This wrapper is used to make the layer immutable, and to make it shareable between multiple +//! `ConfigBag` instances. The frozen layer can be converted back to a `Layer` if there is only a single reference to it. +//! +//! All of `Layer`, `CloneableLayer`, `FrozenLayer`, and `ConfigBag` are considered to be "bag" types. +//! That is, they store arbitrary types so long as they implement the [`Storable`](crate::config_bag::Storable) trait. +//! +//! A `Storable` requires a `Storer` to be configured, and the storer allows for storables to be stored +//! in two different modes: +//! 1. [`StoreReplace`](crate::config_bag::StoreReplace) - Only one value of this type is allowed in a bag, and +//! calling [`store_put()`](crate::config_bag::Layer::store_put) multiple times will replace the existing value +//! in the bag. Calling [`load::()`](crate::config_bag::Layer::load) returns exactly one value, if present. +//! 2. [`StoreAppend`](crate::config_bag::StoreAppend) - Multiple values of this type are allowed in a bag, and +//! calling [`store_append()`](crate::config_bag::Layer::store_append) will add an additional value of this type +//! to the bag. Calling [`load::()`](crate::config_bag::Layer::load) returns an iterator over multiple values. +//! +//! # Examples +//! +//! Creating a storable data type with `StoreReplace`: +//! +//! ```no_run +//! use aws_smithy_types::config_bag::{Storable, StoreReplace}; +//! +//! #[derive(Debug)] +//! struct SomeDataType { +//! some_data: String, +//! } +//! impl Storable for SomeDataType { +//! type Storer = StoreReplace; +//! } +//! ``` +//! +//! Creating a storable data type with `StoreAppend`: +//! +//! ```no_run +//! use aws_smithy_types::config_bag::{Storable, StoreAppend}; +//! +//! #[derive(Debug)] +//! struct SomeDataType { +//! some_data: String, +//! } +//! impl Storable for SomeDataType { +//! type Storer = StoreAppend; +//! } +//! ``` +//! +//! Storing a storable in a bag when it is configured for `StoreReplace`: +//! +//! ```no_run +//! # use aws_smithy_types::config_bag::{Storable, StoreReplace}; +//! # #[derive(Clone, Debug)] +//! # struct SomeDataType { some_data: String } +//! # impl Storable for SomeDataType { type Storer = StoreReplace; } +//! use aws_smithy_types::config_bag::{CloneableLayer, Layer}; +//! +//! let mut layer = Layer::new("example"); +//! layer.store_put(SomeDataType { some_data: "some data".to_string() }); +//! +//! // `store_put` can be called again to replace the original value: +//! layer.store_put(SomeDataType { some_data: "replacement".to_string() }); +//! +//! // Note: `SomeDataType` below must implement `Clone` to work with `CloneableLayer` +//! let mut cloneable = CloneableLayer::new("example"); +//! cloneable.store_put(SomeDataType { some_data: "some data".to_string() }); +//! ``` +//! +//! Storing a storable in a bag when it is configured for `StoreAppend`: +//! +//! ```no_run +//! # use aws_smithy_types::config_bag::{Storable, StoreAppend}; +//! # #[derive(Clone, Debug)] +//! # struct SomeDataType { some_data: String } +//! # impl Storable for SomeDataType { type Storer = StoreAppend; } +//! use aws_smithy_types::config_bag::{CloneableLayer, Layer}; +//! +//! let mut layer = Layer::new("example"); +//! layer.store_append(SomeDataType { some_data: "1".to_string() }); +//! layer.store_append(SomeDataType { some_data: "2".to_string() }); +//! ``` +//! +//! Loading a `StoreReplace` value from a bag: +//! +//! ```no_run +//! # use aws_smithy_types::config_bag::{Storable, StoreReplace}; +//! # #[derive(Clone, Debug)] +//! # struct SomeDataType { some_data: String } +//! # impl Storable for SomeDataType { type Storer = StoreReplace; } +//! # use aws_smithy_types::config_bag::Layer; +//! # let layer = Layer::new("example"); +//! let maybe_value: Option<&SomeDataType> = layer.load::(); +//! ``` +//! +//! Loading a `StoreAppend` value from a bag: +//! +//! ```no_run +//! # use aws_smithy_types::config_bag::{Storable, StoreAppend}; +//! # #[derive(Clone, Debug)] +//! # struct SomeDataType { some_data: String } +//! # impl Storable for SomeDataType { type Storer = StoreAppend; } +//! # use aws_smithy_types::config_bag::Layer; +//! # let layer = Layer::new("example"); +//! let values: Vec = layer.load::().cloned().collect(); +//! +//! // or iterate over them directly: +//! for value in layer.load::() { +//! # let _ = value; +//! // ... +//! } +//! ``` +//! +mod storable; +mod typeid_map; + +use crate::config_bag::typeid_map::TypeIdMap; +use crate::type_erasure::TypeErasedBox; +use std::any::{type_name, TypeId}; +use std::borrow::Cow; +use std::fmt::{Debug, Formatter}; +use std::iter::Rev; +use std::marker::PhantomData; +use std::ops::Deref; +use std::slice::Iter; +use std::sync::Arc; + +pub use storable::{AppendItemIter, Storable, Store, StoreAppend, StoreReplace}; + +/// [`FrozenLayer`] is the immutable and shareable form of [`Layer`]. +/// +/// See the [module docs](crate::config_bag) for more documentation. +#[derive(Clone, Debug)] +#[must_use] +pub struct FrozenLayer(Arc); + +impl FrozenLayer { + /// Attempts to convert this bag directly into a [`Layer`] if no other references exist. + pub fn try_modify(self) -> Option { + Arc::try_unwrap(self.0).ok() + } +} + +impl Deref for FrozenLayer { + type Target = Layer; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for FrozenLayer { + fn from(layer: Layer) -> Self { + FrozenLayer(Arc::new(layer)) + } +} + +/// Private module to keep Value type while avoiding "private type in public latest" +pub(crate) mod value { + #[derive(Clone, Debug)] + pub enum Value { + Set(T), + ExplicitlyUnset(&'static str), + } + + impl Default for Value { + fn default() -> Self { + Self::Set(Default::default()) + } + } +} +use value::Value; + +/// [`CloneableLayer`] allows itself to be cloned. This is useful when a type that implements +/// `Clone` wishes to store a config layer. +/// +/// It ensures that all the items in `CloneableLayer` are `Clone` upon entry, e.g. when they are +/// first stored, the mutable methods require that they have a `Clone` bound on them. +/// +/// While [`FrozenLayer`] is also cloneable, which is a shallow clone via `Arc`, `CloneableLayer` +/// performs a deep clone that newly allocates all the items stored in it. +/// +/// Cloneable enforces that non clone items cannot be added +/// ```rust,compile_fail +/// use aws_smithy_types::config_bag::Storable; +/// use aws_smithy_types::config_bag::StoreReplace; +/// use aws_smithy_types::config_bag::CloneableLayer; +/// #[derive(Debug)] +/// struct MyNotCloneStruct; +/// +/// impl Storable for MyNotCloneStruct { +/// type Storer = StoreReplace; +/// } +/// let mut layer = CloneableLayer::new("layer"); +/// layer.store_put(MyNotCloneStruct); +/// ``` +/// +/// See the [module docs](crate::config_bag) for more documentation. +#[derive(Debug, Default)] +pub struct CloneableLayer(Layer); + +impl Deref for CloneableLayer { + type Target = Layer; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Clone for CloneableLayer { + fn clone(&self) -> Self { + Self( + self.try_clone() + .expect("only cloneable types can be inserted"), + ) + } +} + +impl From for Layer { + fn from(cloneable_layer: CloneableLayer) -> Layer { + cloneable_layer.0 + } +} + +// We need to "override" the mutable methods to encode the information that an item being stored +// implements `Clone`. For the immutable methods, they can just be delegated via the `Deref` trait. +impl CloneableLayer { + /// Creates a new `CloneableLayer` with a given name + pub fn new(name: impl Into>) -> Self { + Self(Layer::new(name)) + } + + /// Converts this layer into a frozen layer that can no longer be mutated. + pub fn freeze(self) -> FrozenLayer { + self.0.into() + } + + /// Removes `T` from this bag + pub fn unset(&mut self) -> &mut Self { + self.put_directly_cloneable::>(Value::ExplicitlyUnset(type_name::())); + self + } + + fn put_directly_cloneable(&mut self, value: T::StoredType) -> &mut Self + where + T::StoredType: Clone, + { + self.0 + .props + .insert(TypeId::of::(), TypeErasedBox::new_with_clone(value)); + self + } + + /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type + pub fn store_put(&mut self, item: T) -> &mut Self + where + T: Storable> + Clone, + { + self.put_directly_cloneable::>(Value::Set(item)); + self + } + + /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type, + /// or unsets it by passing a `None` + pub fn store_or_unset(&mut self, item: Option) -> &mut Self + where + T: Storable> + Clone, + { + let item = match item { + Some(item) => Value::Set(item), + None => Value::ExplicitlyUnset(type_name::()), + }; + self.put_directly_cloneable::>(item); + self + } + + /// Stores `item` of type `T` into the config bag, appending it to the existing list of the same + /// type + pub fn store_append(&mut self, item: T) -> &mut Self + where + T: Storable> + Clone, + { + match self.get_mut_or_default::>() { + Value::Set(list) => list.push(item), + v @ Value::ExplicitlyUnset(_) => *v = Value::Set(vec![item]), + } + self + } + + /// Clears the value of type `T` from the config bag + pub fn clear(&mut self) + where + T: Storable> + Clone, + { + self.put_directly_cloneable::>(Value::ExplicitlyUnset(type_name::())); + } + + fn get_mut_or_default(&mut self) -> &mut T::StoredType + where + T::StoredType: Default + Clone, + { + self.0 + .props + .entry(TypeId::of::()) + .or_insert_with(|| TypeErasedBox::new_with_clone(T::StoredType::default())) + .downcast_mut() + .expect("typechecked") + } +} + +/// A named layer comprising a config bag +/// +/// See the [module docs](crate::config_bag) for more documentation. +#[derive(Default)] +pub struct Layer { + name: Cow<'static, str>, + props: TypeIdMap, +} + +impl Debug for Layer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + struct Items<'a>(&'a Layer); + impl Debug for Items<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.0.props.values()).finish() + } + } + f.debug_struct("Layer") + .field("name", &self.name) + .field("items", &Items(self)) + .finish() + } +} + +impl Layer { + fn try_clone(&self) -> Option { + let new_props = self + .props + .iter() + .flat_map(|(tyid, erased)| erased.try_clone().map(|e| (*tyid, e))) + .collect::>(); + if new_props.len() == self.props.len() { + Some(Layer { + name: self.name.clone(), + props: new_props, + }) + } else { + None + } + } + + /// Inserts `value` into the layer directly + fn put_directly(&mut self, value: T::StoredType) -> &mut Self { + self.props + .insert(TypeId::of::(), TypeErasedBox::new(value)); + self + } + + /// Returns true if this layer is empty. + pub fn is_empty(&self) -> bool { + self.props.is_empty() + } + + /// Converts this layer into a frozen layer that can no longer be mutated. + pub fn freeze(self) -> FrozenLayer { + self.into() + } + + /// Create a new Layer with a given name + pub fn new(name: impl Into>) -> Self { + let name = name.into(); + Self { + name, + props: Default::default(), + } + } + + /// Changes the name of this layer. + pub fn with_name(self, name: impl Into>) -> Self { + Self { + name: name.into(), + props: self.props, + } + } + + /// Load a storable item from the bag + pub fn load(&self) -> ::ReturnedType<'_> { + T::Storer::merge_iter(ItemIter { + inner: BagIter { + head: Some(self), + tail: [].iter().rev(), + }, + t: Default::default(), + }) + } + + /// Remove `T` from this bag + pub fn unset(&mut self) -> &mut Self { + self.put_directly::>(Value::ExplicitlyUnset(type_name::())); + self + } + + /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type + pub fn store_put(&mut self, item: T) -> &mut Self + where + T: Storable>, + { + self.put_directly::>(Value::Set(item)); + self + } + + /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type, + /// or unsets it by passing a `None` + pub fn store_or_unset(&mut self, item: Option) -> &mut Self + where + T: Storable>, + { + let item = match item { + Some(item) => Value::Set(item), + None => Value::ExplicitlyUnset(type_name::()), + }; + self.put_directly::>(item); + self + } + + /// This can only be used for types that use [`StoreAppend`] + /// ``` + /// use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreAppend, StoreReplace}; + /// let mut layer_1 = Layer::new("example"); + /// #[derive(Debug, PartialEq, Eq)] + /// struct Interceptor(&'static str); + /// impl Storable for Interceptor { + /// type Storer = StoreAppend; + /// } + /// + /// layer_1.store_append(Interceptor("321")); + /// layer_1.store_append(Interceptor("654")); + /// + /// let mut layer_2 = Layer::new("second layer"); + /// layer_2.store_append(Interceptor("987")); + /// + /// let bag = ConfigBag::of_layers(vec![layer_1, layer_2]); + /// + /// assert_eq!( + /// bag.load::().collect::>(), + /// vec![&Interceptor("987"), &Interceptor("654"), &Interceptor("321")] + /// ); + /// ``` + pub fn store_append(&mut self, item: T) -> &mut Self + where + T: Storable>, + { + match self.get_mut_or_default::>() { + Value::Set(list) => list.push(item), + v @ Value::ExplicitlyUnset(_) => *v = Value::Set(vec![item]), + } + self + } + + /// Clears the value of type `T` from the config bag + /// + /// This internally marks the item of type `T` as cleared as opposed to wiping it out from the + /// config bag. + pub fn clear(&mut self) + where + T: Storable>, + { + self.put_directly::>(Value::ExplicitlyUnset(type_name::())); + } + + /// Retrieves the value of type `T` from this layer if exists + fn get(&self) -> Option<&T::StoredType> { + self.props + .get(&TypeId::of::()) + .map(|t| t.downcast_ref().expect("typechecked")) + } + + /// Returns a mutable reference to `T` if it is stored in this layer + fn get_mut(&mut self) -> Option<&mut T::StoredType> { + self.props + .get_mut(&TypeId::of::()) + .map(|t| t.downcast_mut().expect("typechecked")) + } + + /// Returns a mutable reference to `T` if it is stored in this layer, otherwise returns the + /// [`Default`] implementation of `T` + fn get_mut_or_default(&mut self) -> &mut T::StoredType + where + T::StoredType: Default, + { + self.props + .entry(TypeId::of::()) + .or_insert_with(|| TypeErasedBox::new(T::StoredType::default())) + .downcast_mut() + .expect("typechecked") + } +} + +/// Layered configuration structure +/// +/// See the [module docs](crate::config_bag) for more documentation. +#[must_use] +pub struct ConfigBag { + interceptor_state: Layer, + tail: Vec, +} + +impl Debug for ConfigBag { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + struct Layers<'a>(&'a ConfigBag); + impl Debug for Layers<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.0.layers()).finish() + } + } + f.debug_struct("ConfigBag") + .field("layers", &Layers(self)) + .finish() + } +} + +impl ConfigBag { + /// Create a new config bag "base". + pub fn base() -> Self { + ConfigBag { + interceptor_state: Layer { + name: Cow::Borrowed("interceptor_state"), + props: Default::default(), + }, + tail: vec![], + } + } + + /// Create a [`ConfigBag`] consisting of the given layers. + pub fn of_layers(layers: impl IntoIterator) -> Self { + let mut bag = ConfigBag::base(); + for layer in layers { + bag.push_layer(layer); + } + bag + } + + /// Add the given layer to the config bag. + pub fn push_layer(&mut self, layer: Layer) -> &mut Self { + self.tail.push(layer.freeze()); + self + } + + /// Add a frozen/shared layer to the config bag. + pub fn push_shared_layer(&mut self, layer: FrozenLayer) -> &mut Self { + self.tail.push(layer); + self + } + + /// Return a reference to the mutable interceptor state. + pub fn interceptor_state(&mut self) -> &mut Layer { + &mut self.interceptor_state + } + + /// Load a value (or values) of type `T` depending on how `T` implements [`Storable`] + pub fn load(&self) -> ::ReturnedType<'_> { + self.sourced_get::() + } + + /// Return a mutable reference to `T` if it is stored in the top layer of the bag + pub fn get_mut(&mut self) -> Option<&mut T> + where + T: Storable>, + { + // this code looks weird to satisfy the borrow checker—we can't keep the result of `get_mut` + // alive (even in a returned branch) and then call `store_put`. So: drop the borrow immediately + // store, the value, then pull it right back + if matches!(self.interceptor_state.get_mut::>(), None) { + let new_item = match self.tail.iter().find_map(|b| b.load::()) { + Some(item) => item.clone(), + None => return None, + }; + self.interceptor_state.store_put(new_item); + self.get_mut() + } else if matches!( + self.interceptor_state.get::>(), + Some(Value::ExplicitlyUnset(_)) + ) { + None + } else if let Some(Value::Set(t)) = self.interceptor_state.get_mut::>() { + Some(t) + } else { + unreachable!() + } + } + + /// Returns a mutable reference to `T` if it is stored in the top layer of the bag + /// + /// - If `T` is in a deeper layer of the bag, that value will be cloned and inserted into the top layer + /// - If `T` is not present in the bag, the [`Default`] implementation will be used. + pub fn get_mut_or_default( + &mut self, + ) -> &mut T + where + T: Storable>, + { + self.get_mut_or_else(|| T::default()) + } + + /// Returns a mutable reference to `T` if it is stored in the top layer of the bag + /// + /// - If `T` is in a deeper layer of the bag, that value will be cloned and inserted into the top layer + /// - If `T` is not present in the bag, `default` will be used to construct a new value + pub fn get_mut_or_else( + &mut self, + default: impl Fn() -> T, + ) -> &mut T + where + T: Storable>, + { + // this code looks weird to satisfy the borrow checker—we can't keep the result of `get_mut` + // alive (even in a returned branch) and then call `store_put`. So: drop the borrow immediately + // store, the value, then pull it right back + if self.get_mut::().is_none() { + self.interceptor_state.store_put((default)()); + return self + .get_mut() + .expect("item was just stored in the top layer"); + } + // above it was None + self.get_mut().unwrap() + } + + /// Add another layer to this configuration bag + /// + /// Hint: If you want to re-use this layer, call `freeze` first. + /// + /// # Examples + /// ``` + /// use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; + /// + /// #[derive(Debug, Eq, PartialEq)] + /// struct ExampleStr(&'static str); + /// impl Storable for ExampleStr { + /// type Storer = StoreReplace; + /// } + /// + /// #[derive(Debug, Eq, PartialEq)] + /// struct ExampleInt(i32); + /// impl Storable for ExampleInt { + /// type Storer = StoreReplace; + /// } + /// + /// let mut bag = ConfigBag::base(); + /// bag = bag.with_fn("first", |layer: &mut Layer| { layer.store_put(ExampleStr("a")); }); + /// + /// // We can now load the example string out + /// assert_eq!(bag.load::(), Some(&ExampleStr("a"))); + /// + /// // But there isn't a number stored in the bag yet + /// assert_eq!(bag.load::(), None); + /// + /// // Add a layer with an example int + /// bag = bag.with_fn("second", |layer: &mut Layer| { layer.store_put(ExampleInt(1)); }); + /// + /// // Now the example int can be retrieved + /// assert_eq!(bag.load::(), Some(&ExampleInt(1))); + /// ``` + pub fn with_fn( + self, + name: impl Into>, + next: impl Fn(&mut Layer), + ) -> ConfigBag { + let mut new_layer = Layer::new(name); + next(&mut new_layer); + let ConfigBag { + interceptor_state: head, + mut tail, + } = self; + tail.push(head.freeze()); + ConfigBag { + interceptor_state: new_layer, + tail, + } + } + + /// Add a new layer with `name` after freezing the top layer so far + pub fn add_layer(self, name: impl Into>) -> ConfigBag { + self.with_fn(name, |_| {}) + } + + /// Return a value (or values) of type `T` depending on how it has been stored in a `ConfigBag` + /// + /// It flexibly chooses to return a single value vs. an iterator of values depending on how + /// `T` implements a [`Store`] trait. + pub fn sourced_get(&self) -> T::ReturnedType<'_> { + let stored_type_iter = ItemIter { + inner: self.layers(), + t: PhantomData::default(), + }; + T::merge_iter(stored_type_iter) + } + + fn layers(&self) -> BagIter<'_> { + BagIter { + head: Some(&self.interceptor_state), + tail: self.tail.iter().rev(), + } + } +} + +/// Iterator of items returned from [`ConfigBag`]. +pub struct ItemIter<'a, T> { + inner: BagIter<'a>, + t: PhantomData, +} + +impl<'a, T> Debug for ItemIter<'a, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "ItemIter") + } +} + +impl<'a, T: 'a> Iterator for ItemIter<'a, T> +where + T: Store, +{ + type Item = &'a T::StoredType; + + fn next(&mut self) -> Option { + match self.inner.next() { + Some(layer) => layer.get::().or_else(|| self.next()), + None => None, + } + } +} + +/// Iterator over the layers of a config bag +struct BagIter<'a> { + head: Option<&'a Layer>, + tail: Rev>, +} + +impl<'a> Iterator for BagIter<'a> { + type Item = &'a Layer; + + fn next(&mut self) -> Option { + if let Some(head) = self.head.take() { + Some(head) + } else { + self.tail.next().map(|t| t.deref()) + } + } +} + +#[cfg(test)] +mod test { + use super::ConfigBag; + use crate::config_bag::{CloneableLayer, Layer, Storable, StoreAppend, StoreReplace}; + + #[test] + fn layered_property_bag() { + #[derive(Debug)] + struct Prop1; + impl Storable for Prop1 { + type Storer = StoreReplace; + } + #[derive(Debug)] + struct Prop2; + impl Storable for Prop2 { + type Storer = StoreReplace; + } + let layer_a = |bag: &mut Layer| { + bag.store_put(Prop1); + }; + + let layer_b = |bag: &mut Layer| { + bag.store_put(Prop2); + }; + + #[derive(Debug)] + struct Prop3; + impl Storable for Prop3 { + type Storer = StoreReplace; + } + + let mut base_bag = ConfigBag::base() + .with_fn("a", layer_a) + .with_fn("b", layer_b); + base_bag.interceptor_state().store_put(Prop3); + assert!(base_bag.load::().is_some()); + + #[derive(Debug)] + struct Prop4; + impl Storable for Prop4 { + type Storer = StoreReplace; + } + + let layer_c = |bag: &mut Layer| { + bag.store_put(Prop4); + bag.unset::(); + }; + + let final_bag = base_bag.with_fn("c", layer_c); + + assert!(final_bag.load::().is_some()); + assert!(final_bag.load::().is_some()); + assert!(final_bag.load::().is_some()); + // we unset prop3 + assert!(final_bag.load::().is_none()); + println!("{:#?}", final_bag); + } + + #[test] + fn config_bag() { + let bag = ConfigBag::base(); + #[derive(Debug)] + struct Region(&'static str); + impl Storable for Region { + type Storer = StoreReplace; + } + let bag = bag.with_fn("service config", |layer: &mut Layer| { + layer.store_put(Region("asdf")); + }); + + assert_eq!(bag.load::().unwrap().0, "asdf"); + + #[derive(Debug)] + struct SigningName(&'static str); + impl Storable for SigningName { + type Storer = StoreReplace; + } + let operation_config = bag.with_fn("operation", |layer: &mut Layer| { + layer.store_put(SigningName("s3")); + }); + + assert_eq!(operation_config.load::().unwrap().0, "s3"); + + #[derive(Debug)] + struct Prop; + impl Storable for Prop { + type Storer = StoreReplace; + } + let mut open_bag = operation_config.with_fn("my_custom_info", |_bag: &mut Layer| {}); + open_bag.interceptor_state().store_put(Prop); + + assert_eq!(open_bag.layers().count(), 4); + } + + #[test] + fn store_append() { + let mut layer = Layer::new("test"); + #[derive(Debug, PartialEq, Eq)] + struct Interceptor(&'static str); + impl Storable for Interceptor { + type Storer = StoreAppend; + } + + layer.clear::(); + // you can only call store_append because interceptor is marked with a vec + layer.store_append(Interceptor("123")); + layer.store_append(Interceptor("456")); + + let mut second_layer = Layer::new("next"); + second_layer.store_append(Interceptor("789")); + + let mut bag = ConfigBag::of_layers(vec![layer, second_layer]); + + assert_eq!( + bag.load::().collect::>(), + vec![ + &Interceptor("789"), + &Interceptor("456"), + &Interceptor("123") + ] + ); + + let mut final_layer = Layer::new("final"); + final_layer.clear::(); + bag.push_layer(final_layer); + assert_eq!(bag.load::().count(), 0); + } + + #[test] + fn store_append_many_layers() { + #[derive(Debug, PartialEq, Eq, Clone)] + struct TestItem(i32, i32); + impl Storable for TestItem { + type Storer = StoreAppend; + } + let mut expected = vec![]; + let mut bag = ConfigBag::base(); + for layer_idx in 0..100 { + let mut layer = Layer::new(format!("{}", layer_idx)); + for item in 0..100 { + expected.push(TestItem(layer_idx, item)); + layer.store_append(TestItem(layer_idx, item)); + } + bag.push_layer(layer); + } + expected.reverse(); + assert_eq!( + bag.load::().cloned().collect::>(), + expected + ); + } + + #[test] + fn adding_layers() { + let mut layer_1 = Layer::new("layer1"); + + let mut layer_2 = Layer::new("layer2"); + + #[derive(Clone, Debug, PartialEq, Eq, Default)] + struct Foo(usize); + impl Storable for Foo { + type Storer = StoreReplace; + } + + layer_1.store_put(Foo(0)); + layer_2.store_put(Foo(1)); + + let layer_1 = layer_1.freeze(); + let layer_2 = layer_2.freeze(); + + let mut bag_1 = ConfigBag::base(); + let mut bag_2 = ConfigBag::base(); + bag_1 + .push_shared_layer(layer_1.clone()) + .push_shared_layer(layer_2.clone()); + bag_2.push_shared_layer(layer_2).push_shared_layer(layer_1); + + // bags have same layers but in different orders + assert_eq!(bag_1.load::(), Some(&Foo(1))); + assert_eq!(bag_2.load::(), Some(&Foo(0))); + + bag_1.interceptor_state().store_put(Foo(3)); + assert_eq!(bag_1.load::(), Some(&Foo(3))); + } + + #[test] + fn get_mut_or_else() { + #[derive(Clone, Debug, PartialEq, Eq, Default)] + struct Foo(usize); + impl Storable for Foo { + type Storer = StoreReplace; + } + + let mut bag = ConfigBag::base(); + assert_eq!(bag.get_mut::(), None); + assert_eq!(bag.get_mut_or_default::(), &Foo(0)); + bag.get_mut_or_default::().0 += 1; + assert_eq!(bag.load::(), Some(&Foo(1))); + + let old_ref = bag.load::().unwrap(); + assert_eq!(old_ref, &Foo(1)); + + // there is one in the bag, so it can be returned + //let mut next = bag.add_layer("next"); + bag.get_mut::().unwrap().0 += 1; + let new_ref = bag.load::().unwrap(); + assert_eq!(new_ref, &Foo(2)); + + bag.interceptor_state().unset::(); + // if it was unset, we can't clone the current one, that would be wrong + assert_eq!(bag.get_mut::(), None); + assert_eq!(bag.get_mut_or_default::(), &Foo(0)); + } + + #[test] + fn cloning_layers() { + #[derive(Clone, Debug)] + struct TestStr(String); + impl Storable for TestStr { + type Storer = StoreReplace; + } + let mut layer_1 = CloneableLayer::new("layer_1"); + let expected_str = "I can be cloned"; + layer_1.store_put(TestStr(expected_str.to_owned())); + let layer_1_cloned = layer_1.clone(); + assert_eq!(expected_str, &layer_1_cloned.load::().unwrap().0); + + // Should still be cloneable after unsetting a field + layer_1.unset::(); + assert!(layer_1.try_clone().unwrap().load::().is_none()); + + // It is cloneable multiple times in succession + let _ = layer_1 + .try_clone() + .expect("clone 1") + .try_clone() + .expect("clone 2"); + + #[derive(Clone, Debug)] + struct Rope(String); + impl Storable for Rope { + type Storer = StoreAppend; + } + let mut layer_2 = CloneableLayer::new("layer_2"); + layer_2.store_append(Rope("A".to_owned())); + layer_2.store_append(Rope("big".to_owned())); + layer_2.store_append(Rope("rope".to_owned())); + let layer_2_cloned = layer_2.clone(); + let rope = layer_2_cloned.load::().cloned().collect::>(); + assert_eq!( + "A big rope", + rope.iter() + .rev() + .map(|r| r.0.clone()) + .collect::>() + .join(" ") + ); + } +} diff --git a/rust-runtime/aws-smithy-types/src/config_bag/storable.rs b/rust-runtime/aws-smithy-types/src/config_bag/storable.rs new file mode 100644 index 0000000000..3a70e422ea --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/config_bag/storable.rs @@ -0,0 +1,114 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::config_bag::value::Value; +use crate::config_bag::ItemIter; +use std::fmt::{Debug, Formatter}; +use std::iter::Rev; +use std::marker::PhantomData; +use std::slice; + +/// Trait defining how types can be stored and loaded from the config bag +pub trait Store: Sized + Send + Sync + 'static { + /// Denote the returned type when loaded from the config bag + type ReturnedType<'a>: Send + Sync; + /// Denote the stored type when stored into the config bag + type StoredType: Send + Sync + Debug; + + /// Create a returned type from an iterable of items + fn merge_iter(iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_>; +} + +/// Store an item in the config bag by replacing the existing value +/// +/// See the [module docs](crate::config_bag) for more documentation. +#[non_exhaustive] +pub struct StoreReplace(PhantomData); + +impl Debug for StoreReplace { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "StoreReplace") + } +} + +/// Store an item in the config bag by effectively appending it to a list +/// +/// See the [module docs](crate::config_bag) for more documentation. +#[non_exhaustive] +pub struct StoreAppend(PhantomData); + +impl Debug for StoreAppend { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "StoreAppend") + } +} + +/// Trait that marks the implementing types as able to be stored in the config bag +/// +/// See the [module docs](crate::config_bag) for more documentation. +pub trait Storable: Send + Sync + Debug + 'static { + /// Specify how an item is stored in the config bag, e.g. [`StoreReplace`] and [`StoreAppend`] + type Storer: Store; +} + +impl Store for StoreReplace { + type ReturnedType<'a> = Option<&'a U>; + type StoredType = Value; + + fn merge_iter(mut iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_> { + iter.next().and_then(|item| match item { + Value::Set(item) => Some(item), + Value::ExplicitlyUnset(_) => None, + }) + } +} + +impl Store for StoreAppend { + type ReturnedType<'a> = AppendItemIter<'a, U>; + type StoredType = Value>; + + fn merge_iter(iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_> { + AppendItemIter { + inner: iter, + cur: None, + } + } +} + +/// Iterator of items returned by [`StoreAppend`] +pub struct AppendItemIter<'a, U> { + inner: ItemIter<'a, StoreAppend>, + cur: Option>>, +} + +impl<'a, U> Debug for AppendItemIter<'a, U> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "AppendItemIter") + } +} + +impl<'a, U: 'a> Iterator for AppendItemIter<'a, U> +where + U: Send + Sync + Debug + 'static, +{ + type Item = &'a U; + + fn next(&mut self) -> Option { + if let Some(buf) = &mut self.cur { + match buf.next() { + Some(item) => return Some(item), + None => self.cur = None, + } + } + match self.inner.next() { + None => None, + Some(Value::Set(u)) => { + self.cur = Some(u.iter().rev()); + self.next() + } + Some(Value::ExplicitlyUnset(_)) => None, + } + } +} diff --git a/rust-runtime/aws-smithy-types/src/config_bag/typeid_map.rs b/rust-runtime/aws-smithy-types/src/config_bag/typeid_map.rs new file mode 100644 index 0000000000..68818c9b58 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/config_bag/typeid_map.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::any::TypeId; +use std::collections::HashMap; +use std::hash::{BuildHasherDefault, Hasher}; + +pub(super) type TypeIdMap = HashMap>; + +// With TypeIds as keys, there's no need to hash them. They are already hashes +// themselves, coming from the compiler. The IdHasher just holds the u64 of +// the TypeId, and then returns it, instead of doing any bit fiddling. +#[derive(Default)] +pub(super) struct IdHasher(u64); + +impl Hasher for IdHasher { + #[inline] + fn finish(&self) -> u64 { + self.0 + } + + fn write(&mut self, _: &[u8]) { + unreachable!("TypeId calls write_u64"); + } + + #[inline] + fn write_u64(&mut self, id: u64) { + self.0 = id; + } +} diff --git a/rust-runtime/aws-smithy-types/src/date_time/de.rs b/rust-runtime/aws-smithy-types/src/date_time/de.rs new file mode 100644 index 0000000000..c87a32e0f5 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/date_time/de.rs @@ -0,0 +1,140 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::*; +use serde::de::{Error, Visitor}; +use serde::Deserialize; + +struct DateTimeVisitor; + +struct NonHumanReadableDateTimeVisitor; + +impl<'de> Visitor<'de> for DateTimeVisitor { + type Value = DateTime; + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("expected RFC-3339 Date Time") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match DateTime::from_str(v, Format::DateTime) { + Ok(e) => Ok(e), + Err(e) => Err(Error::custom(e)), + } + } +} + +impl<'de> Visitor<'de> for NonHumanReadableDateTimeVisitor { + type Value = DateTime; + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("DateTime type expects a tuple of i64 and u32 when deserializing from non human readable format like CBOR or AVRO, i.e. (i64, u32)") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + match seq.size_hint() { + Some(2) | None => match (seq.next_element()?, seq.next_element()?) { + (Some(seconds), Some(subsecond_nanos)) => Ok(DateTime { + seconds, + subsecond_nanos, + }), + _ => return Err(Error::custom("datatype mismatch")), + }, + _ => Err(Error::custom("Size mismatch")), + } + } +} + +impl<'de> Deserialize<'de> for DateTime { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + deserializer.deserialize_str(DateTimeVisitor) + } else { + deserializer.deserialize_tuple(2, NonHumanReadableDateTimeVisitor) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + /// check for human redable format + #[test] + fn de_human_readable_datetime() { + use serde::{Deserialize, Serialize}; + + let datetime = DateTime::from_secs(1576540098); + #[derive(Serialize, Deserialize, PartialEq)] + struct Test { + datetime: DateTime, + } + let datetime_json = r#"{"datetime":"2019-12-16T23:48:18Z"}"#; + let test = serde_json::from_str::(&datetime_json).ok(); + assert!(test == Some(Test { datetime })); + } + + /// check for non-human redable format + #[test] + fn de_not_human_readable_datetime() { + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(1576540098i64.into()), + ciborium::value::Value::Integer(0u32.into()), + ]); + let mut buf = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let cbor_dt: DateTime = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap(); + assert_eq!( + cbor_dt, + DateTime { + seconds: 1576540098i64, + subsecond_nanos: 0 + } + ); + }; + + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(0i64.into()), + ciborium::value::Value::Integer(0u32.into()), + ]); + let mut buf = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let cbor_dt: DateTime = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap(); + assert_eq!( + cbor_dt, + DateTime { + seconds: 0, + subsecond_nanos: 0 + } + ); + }; + + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(i64::MAX.into()), + ciborium::value::Value::Integer(u32::MAX.into()), + ]); + let mut buf = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let cbor_dt: DateTime = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap(); + assert_eq!( + cbor_dt, + DateTime { + seconds: i64::MAX, + subsecond_nanos: u32::MAX + } + ); + }; + } +} diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index 0459619998..b5e740cf5b 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -9,6 +9,7 @@ use crate::date_time::format::rfc3339::AllowOffsets; use crate::date_time::format::DateTimeParseErrorKind; use num_integer::div_mod_floor; use num_integer::Integer; +use std::cmp::Ordering; use std::convert::TryFrom; use std::error::Error as StdError; use std::fmt; @@ -16,7 +17,12 @@ use std::time::Duration; use std::time::SystemTime; use std::time::UNIX_EPOCH; +#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))] +mod de; mod format; +#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))] +mod ser; + pub use self::format::DateTimeFormatError; pub use self::format::DateTimeParseError; @@ -50,8 +56,11 @@ const NANOS_PER_SECOND_U32: u32 = 1_000_000_000; /// [`time`](https://crates.io/crates/time) or [`chrono`](https://crates.io/crates/chrono). #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub struct DateTime { - seconds: i64, - subsecond_nanos: u32, + pub(crate) seconds: i64, + /// Subsecond nanos always advances the wallclock time, even for times where seconds is negative + /// + /// Bigger subsecond nanos => later time + pub(crate) subsecond_nanos: u32, } /* ANCHOR_END: date_time */ @@ -87,12 +96,7 @@ impl DateTime { /// Returns the number of nanoseconds since the Unix epoch that this `DateTime` represents. pub fn as_nanos(&self) -> i128 { let seconds = self.seconds as i128 * NANOS_PER_SECOND; - if seconds < 0 { - let adjusted_nanos = self.subsecond_nanos as i128 - NANOS_PER_SECOND; - seconds + NANOS_PER_SECOND + adjusted_nanos - } else { - seconds + self.subsecond_nanos as i128 - } + seconds + self.subsecond_nanos as i128 } /// Creates a `DateTime` from a number of seconds and a fractional second since the Unix epoch. @@ -175,6 +179,12 @@ impl DateTime { self.seconds } + /// Set the seconds component of this `DateTime`. + pub fn set_seconds(&mut self, seconds: i64) -> &mut Self { + self.seconds = seconds; + self + } + /// Returns the sub-second nanos component of the `DateTime`. /// /// _Note: this does not include the number of seconds since the epoch._ @@ -182,6 +192,12 @@ impl DateTime { self.subsecond_nanos } + /// Set the "sub-second" nanoseconds of this `DateTime`. + pub fn set_subsec_nanos(&mut self, subsec_nanos: u32) -> &mut Self { + self.subsecond_nanos = subsec_nanos; + self + } + /// Converts the `DateTime` to the number of milliseconds since the Unix epoch. /// /// This is fallible since `DateTime` holds more precision than an `i64`, and will @@ -301,6 +317,18 @@ impl From for DateTime { } } +impl PartialOrd for DateTime { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for DateTime { + fn cmp(&self, other: &Self) -> Ordering { + self.as_nanos().cmp(&other.as_nanos()) + } +} + /// Failure to convert a `DateTime` to or from another type. #[derive(Debug)] #[non_exhaustive] @@ -317,16 +345,20 @@ impl fmt::Display for ConversionError { /// Formats for representing a `DateTime` in the Smithy protocols. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Format { - /// RFC-3339 Date Time. If the date time has an offset, an error will be returned + /// RFC-3339 Date Time. If the date time has an offset, an error will be returned. + /// e.g. `2019-12-16T23:48:18Z` DateTime, - /// RFC-3339 Date Time. Offsets are supported + /// RFC-3339 Date Time. Offsets are supported. + /// e.g. `2019-12-16T23:48:18+01:00` DateTimeWithOffset, /// Date format used by the HTTP `Date` header, specified in RFC-7231. + /// e.g. `Mon, 16 Dec 2019 23:48:18 GMT` HttpDate, /// Number of seconds since the Unix epoch formatted as a floating point. + /// e.g. `1576540098.52` EpochSeconds, } @@ -334,6 +366,7 @@ pub enum Format { mod test { use crate::date_time::Format; use crate::DateTime; + use proptest::proptest; use std::convert::TryFrom; use std::time::SystemTime; use time::format_description::well_known::Rfc3339; @@ -552,4 +585,63 @@ mod test { SystemTime::try_from(date_time).unwrap() ); } + + #[test] + fn ord() { + let first = DateTime::from_secs_and_nanos(-1, 0); + let second = DateTime::from_secs_and_nanos(-1, 1); + let third = DateTime::from_secs_and_nanos(0, 0); + let fourth = DateTime::from_secs_and_nanos(0, 1); + let fifth = DateTime::from_secs_and_nanos(1, 0); + + assert!(first == first); + assert!(first < second); + assert!(first < third); + assert!(first < fourth); + assert!(first < fifth); + + assert!(second > first); + assert!(second == second); + assert!(second < third); + assert!(second < fourth); + assert!(second < fifth); + + assert!(third > first); + assert!(third > second); + assert!(third == third); + assert!(third < fourth); + assert!(third < fifth); + + assert!(fourth > first); + assert!(fourth > second); + assert!(fourth > third); + assert!(fourth == fourth); + assert!(fourth < fifth); + + assert!(fifth > first); + assert!(fifth > second); + assert!(fifth > third); + assert!(fifth > fourth); + assert!(fifth == fifth); + } + + const MIN_RFC_3339_MILLIS: i64 = -62135596800000; + const MAX_RFC_3339_MILLIS: i64 = 253402300799999; + + // This test uses milliseconds, because `Format::DateTime` does not support nanoseconds. + proptest! { + #[test] + fn ord_proptest( + left_millis in MIN_RFC_3339_MILLIS..MAX_RFC_3339_MILLIS, + right_millis in MIN_RFC_3339_MILLIS..MAX_RFC_3339_MILLIS, + ) { + let left = DateTime::from_millis(left_millis); + let right = DateTime::from_millis(right_millis); + + let left_str = left.fmt(Format::DateTime).unwrap(); + let right_str = right.fmt(Format::DateTime).unwrap(); + + assert_eq!(left.cmp(&right), left_str.cmp(&right_str)); + } + } } diff --git a/rust-runtime/aws-smithy-types/src/date_time/ser.rs b/rust-runtime/aws-smithy-types/src/date_time/ser.rs new file mode 100644 index 0000000000..5ea5f55dcd --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/date_time/ser.rs @@ -0,0 +1,85 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::*; +use serde::ser::SerializeTuple; + +impl serde::Serialize for DateTime { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if serializer.is_human_readable() { + match self.fmt(Format::DateTime) { + Ok(val) => serializer.serialize_str(&val), + Err(e) => Err(serde::ser::Error::custom(e)), + } + } else { + let mut tup_ser = serializer.serialize_tuple(2)?; + tup_ser.serialize_element(&self.seconds)?; + tup_ser.serialize_element(&self.subsecond_nanos)?; + tup_ser.end() + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + /// check for human redable format + #[test] + fn ser_human_readable_datetime() { + use serde::{Deserialize, Serialize}; + + let datetime = DateTime::from_secs(1576540098); + #[derive(Serialize, Deserialize, PartialEq)] + struct Test { + datetime: DateTime, + } + let datetime_json = r#"{"datetime":"2019-12-16T23:48:18Z"}"#; + assert!(serde_json::to_string(&Test { datetime }).ok() == Some(datetime_json.to_string())); + } + + /// check for non-human redable format + #[test] + fn ser_not_human_readable_datetime() { + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(1576540098i64.into()), + ciborium::value::Value::Integer(0u32.into()), + ]); + let mut buf = vec![]; + let mut buf2 = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let _ = ciborium::ser::into_writer(&cbor, &mut buf2); + assert_eq!(buf, buf2); + }; + + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(0i64.into()), + ciborium::value::Value::Integer(0u32.into()), + ]); + let mut buf = vec![]; + let mut buf2 = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let _ = ciborium::ser::into_writer(&cbor, &mut buf2); + assert_eq!(buf, buf2); + }; + + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(i64::MAX.into()), + ciborium::value::Value::Integer(u32::MAX.into()), + ]); + let mut buf = vec![]; + let mut buf2 = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let _ = ciborium::ser::into_writer(&cbor, &mut buf2); + assert_eq!(buf, buf2); + }; + } +} diff --git a/rust-runtime/aws-smithy-types/src/document.rs b/rust-runtime/aws-smithy-types/src/document.rs new file mode 100644 index 0000000000..2f13f895f8 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/document.rs @@ -0,0 +1,287 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::Number; +use std::borrow::Cow; +use std::collections::HashMap; + +#[cfg(any( + all(aws_sdk_unstable, feature = "serde-deserialize"), + all(aws_sdk_unstable, feature = "serde-serialize") +))] +use serde; + +/* ANCHOR: document */ + +/// Document Type +/// +/// Document types represents protocol-agnostic open content that is accessed like JSON data. +/// Open content is useful for modeling unstructured data that has no schema, data that can't be +/// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. +/// The serialization format of a document is an implementation detail of a protocol. +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr( + all(aws_sdk_unstable, feature = "serde-serialize"), + derive(serde::Serialize) +)] +#[cfg_attr( + all(aws_sdk_unstable, feature = "serde-deserialize"), + derive(serde::Deserialize) +)] +#[cfg_attr( + any( + all(aws_sdk_unstable, feature = "serde-deserialize"), + all(aws_sdk_unstable, feature = "serde-serialize") + ), + serde(untagged) +)] +pub enum Document { + /// JSON object + Object(HashMap), + /// JSON array + Array(Vec), + /// JSON number + Number(Number), + /// JSON string + String(String), + /// JSON boolean + Bool(bool), + /// JSON null + Null, +} + +impl Document { + /// Returns the inner map value if this `Document` is an object. + pub fn as_object(&self) -> Option<&HashMap> { + if let Self::Object(object) = self { + Some(object) + } else { + None + } + } + + /// Returns the mutable inner map value if this `Document` is an object. + pub fn as_object_mut(&mut self) -> Option<&mut HashMap> { + if let Self::Object(object) = self { + Some(object) + } else { + None + } + } + + /// Returns the inner array value if this `Document` is an array. + pub fn as_array(&self) -> Option<&Vec> { + if let Self::Array(array) = self { + Some(array) + } else { + None + } + } + + /// Returns the mutable inner array value if this `Document` is an array. + pub fn as_array_mut(&mut self) -> Option<&mut Vec> { + if let Self::Array(array) = self { + Some(array) + } else { + None + } + } + + /// Returns the inner number value if this `Document` is a number. + pub fn as_number(&self) -> Option<&Number> { + if let Self::Number(number) = self { + Some(number) + } else { + None + } + } + + /// Returns the inner string value if this `Document` is a string. + pub fn as_string(&self) -> Option<&str> { + if let Self::String(string) = self { + Some(string) + } else { + None + } + } + + /// Returns the inner boolean value if this `Document` is a boolean. + pub fn as_bool(&self) -> Option { + if let Self::Bool(boolean) = self { + Some(*boolean) + } else { + None + } + } + + /// Returns `Some(())` if this `Document` is a null. + pub fn as_null(&self) -> Option<()> { + if let Self::Null = self { + Some(()) + } else { + None + } + } + + /// Returns `true` if this `Document` is an object. + pub fn is_object(&self) -> bool { + matches!(self, Self::Object(_)) + } + + /// Returns `true` if this `Document` is an array. + pub fn is_array(&self) -> bool { + matches!(self, Self::Array(_)) + } + + /// Returns `true` if this `Document` is a number. + pub fn is_number(&self) -> bool { + matches!(self, Self::Number(_)) + } + + /// Returns `true` if this `Document` is a string. + pub fn is_string(&self) -> bool { + matches!(self, Self::String(_)) + } + + /// Returns `true` if this `Document` is a bool. + pub fn is_bool(&self) -> bool { + matches!(self, Self::Bool(_)) + } + + /// Returns `true` if this `Document` is a boolean. + pub fn is_null(&self) -> bool { + matches!(self, Self::Null) + } +} + +/// The default value is `Document::Null`. +impl Default for Document { + fn default() -> Self { + Self::Null + } +} + +impl From for Document { + fn from(value: bool) -> Self { + Document::Bool(value) + } +} + +impl<'a> From<&'a str> for Document { + fn from(value: &'a str) -> Self { + Document::String(value.to_string()) + } +} + +impl<'a> From> for Document { + fn from(value: Cow<'a, str>) -> Self { + Document::String(value.into_owned()) + } +} + +impl From for Document { + fn from(value: String) -> Self { + Document::String(value) + } +} + +impl From> for Document { + fn from(values: Vec) -> Self { + Document::Array(values) + } +} + +impl From> for Document { + fn from(values: HashMap) -> Self { + Document::Object(values) + } +} + +impl From for Document { + fn from(value: u64) -> Self { + Document::Number(Number::PosInt(value)) + } +} + +impl From for Document { + fn from(value: i64) -> Self { + Document::Number(Number::NegInt(value)) + } +} + +impl From for Document { + fn from(value: i32) -> Self { + Document::Number(Number::NegInt(value as i64)) + } +} + +impl From for Document { + fn from(value: f64) -> Self { + Document::Number(Number::Float(value)) + } +} + +impl From for Document { + fn from(value: Number) -> Self { + Document::Number(value) + } +} + +/* ANCHOR END: document */ + +#[cfg(test)] +mod test { + /// checks if a) serialization of json suceeds and b) it is compatible with serde_json + #[test] + #[cfg(all( + aws_sdk_unstable, + feature = "serde-serialize", + feature = "serde-deserialize" + ))] + fn serialize_json() { + use crate::Document; + use crate::Number; + use std::collections::HashMap; + let mut map: HashMap = HashMap::new(); + // string + map.insert("hello".into(), "world".to_string().into()); + // numbers + map.insert("pos_int".into(), Document::Number(Number::PosInt(1).into())); + map.insert( + "neg_int".into(), + Document::Number(Number::NegInt(-1).into()), + ); + map.insert( + "float".into(), + Document::Number(Number::Float(0.1 + 0.2).into()), + ); + // booleans + map.insert("true".into(), true.into()); + map.insert("false".into(), false.into()); + // check if array with different datatypes would succeed + map.insert( + "array".into(), + vec![ + map.clone().into(), + "hello-world".to_string().into(), + true.into(), + false.into(), + ] + .into(), + ); + // map + map.insert("map".into(), map.clone().into()); + // null + map.insert("null".into(), Document::Null); + let obj = Document::Object(map); + // comparing string isnt going to work since there is no gurantee for the ordering of the keys + let target_file = include_str!("../test_data/serialize_document.json"); + let json: Result = serde_json::from_str(target_file); + // serializer + assert_eq!(serde_json::to_value(&obj).unwrap(), json.unwrap()); + let doc: Result = serde_json::from_str(target_file); + assert_eq!(obj, doc.unwrap()); + } +} diff --git a/rust-runtime/aws-smithy-types/src/endpoint.rs b/rust-runtime/aws-smithy-types/src/endpoint.rs index 8d27b2d55c..5ebf693e5c 100644 --- a/rust-runtime/aws-smithy-types/src/endpoint.rs +++ b/rust-runtime/aws-smithy-types/src/endpoint.rs @@ -4,6 +4,7 @@ */ //! Smithy Endpoint Types +use crate::config_bag::{Storable, StoreReplace}; use crate::Document; use std::borrow::Cow; use std::collections::HashMap; @@ -53,6 +54,10 @@ impl Endpoint { } } +impl Storable for Endpoint { + type Storer = StoreReplace; +} + #[derive(Debug, Clone)] /// Builder for [`Endpoint`] pub struct Builder { diff --git a/rust-runtime/aws-smithy-types/src/error/metadata.rs b/rust-runtime/aws-smithy-types/src/error/metadata.rs index 06925e13f9..6629753733 100644 --- a/rust-runtime/aws-smithy-types/src/error/metadata.rs +++ b/rust-runtime/aws-smithy-types/src/error/metadata.rs @@ -46,6 +46,12 @@ pub struct ErrorMetadata { extras: Option>, } +impl ProvideErrorMetadata for ErrorMetadata { + fn meta(&self) -> &ErrorMetadata { + self + } +} + /// Builder for [`ErrorMetadata`]. #[derive(Debug, Default)] pub struct Builder { diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 2634ff7f39..76fb9010c3 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -13,11 +13,9 @@ rust_2018_idioms, unreachable_pub )] - -use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; -use std::collections::HashMap; - pub mod base64; +/// A typemap for storing configuration. +pub mod config_bag; pub mod date_time; pub mod endpoint; pub mod error; @@ -25,543 +23,20 @@ pub mod primitive; pub mod retry; pub mod timeout; -pub use crate::date_time::DateTime; +/// Utilities for type erasure. +pub mod type_erasure; + +mod blob; +mod document; +mod number; +pub use blob::Blob; +pub use date_time::DateTime; +pub use document::Document; // TODO(deprecated): Remove deprecated re-export /// Use [error::ErrorMetadata] instead. #[deprecated( note = "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`" )] pub use error::ErrorMetadata as Error; - -/// Binary Blob Type -/// -/// Blobs represent protocol-agnostic binary content. -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct Blob { - inner: Vec, -} - -impl Blob { - /// Creates a new blob from the given `input`. - pub fn new>>(input: T) -> Self { - Blob { - inner: input.into(), - } - } - - /// Consumes the `Blob` and returns a `Vec` with its contents. - pub fn into_inner(self) -> Vec { - self.inner - } -} - -impl AsRef<[u8]> for Blob { - fn as_ref(&self) -> &[u8] { - &self.inner - } -} - -/* ANCHOR: document */ - -/// Document Type -/// -/// Document types represents protocol-agnostic open content that is accessed like JSON data. -/// Open content is useful for modeling unstructured data that has no schema, data that can't be -/// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. -/// The serialization format of a document is an implementation detail of a protocol. -#[derive(Debug, Clone, PartialEq)] -pub enum Document { - /// JSON object - Object(HashMap), - /// JSON array - Array(Vec), - /// JSON number - Number(Number), - /// JSON string - String(String), - /// JSON boolean - Bool(bool), - /// JSON null - Null, -} - -impl From for Document { - fn from(value: bool) -> Self { - Document::Bool(value) - } -} - -impl From for Document { - fn from(value: String) -> Self { - Document::String(value) - } -} - -impl From> for Document { - fn from(values: Vec) -> Self { - Document::Array(values) - } -} - -impl From> for Document { - fn from(values: HashMap) -> Self { - Document::Object(values) - } -} - -impl From for Document { - fn from(value: u64) -> Self { - Document::Number(Number::PosInt(value)) - } -} - -impl From for Document { - fn from(value: i64) -> Self { - Document::Number(Number::NegInt(value)) - } -} - -impl From for Document { - fn from(value: i32) -> Self { - Document::Number(Number::NegInt(value as i64)) - } -} - -/// A number type that implements Javascript / JSON semantics, modeled on serde_json: -/// -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Number { - /// Unsigned 64-bit integer value. - PosInt(u64), - /// Signed 64-bit integer value. The wrapped value is _always_ negative. - NegInt(i64), - /// 64-bit floating-point value. - Float(f64), -} - -/* ANCHOR_END: document */ - -impl Number { - /// Converts to an `f64` lossily. - /// Use `Number::try_from` to make the conversion only if it is not lossy. - pub fn to_f64_lossy(self) -> f64 { - match self { - Number::PosInt(v) => v as f64, - Number::NegInt(v) => v as f64, - Number::Float(v) => v, - } - } - - /// Converts to an `f32` lossily. - /// Use `Number::try_from` to make the conversion only if it is not lossy. - pub fn to_f32_lossy(self) -> f32 { - match self { - Number::PosInt(v) => v as f32, - Number::NegInt(v) => v as f32, - Number::Float(v) => v as f32, - } - } -} - -macro_rules! to_unsigned_integer_converter { - ($typ:ident, $styp:expr) => { - #[doc = "Converts to a `"] - #[doc = $styp] - #[doc = "`. This conversion fails if it is lossy."] - impl TryFrom for $typ { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => { - Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) - } - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } - } - }; - - ($typ:ident) => { - to_unsigned_integer_converter!($typ, stringify!($typ)); - }; -} - -macro_rules! to_signed_integer_converter { - ($typ:ident, $styp:expr) => { - #[doc = "Converts to a `"] - #[doc = $styp] - #[doc = "`. This conversion fails if it is lossy."] - impl TryFrom for $typ { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => Ok(Self::try_from(v)?), - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } - } - }; - - ($typ:ident) => { - to_signed_integer_converter!($typ, stringify!($typ)); - }; -} - -/// Converts to a `u64`. The conversion fails if it is lossy. -impl TryFrom for u64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(v), - Number::NegInt(v) => { - Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) - } - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } -} -to_unsigned_integer_converter!(u32); -to_unsigned_integer_converter!(u16); -to_unsigned_integer_converter!(u8); - -impl TryFrom for i64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => Ok(v), - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } -} -to_signed_integer_converter!(i32); -to_signed_integer_converter!(i16); -to_signed_integer_converter!(i8); - -/// Converts to an `f64`. The conversion fails if it is lossy. -impl TryFrom for f64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - // Integers can only be represented with full precision in a float if they fit in the - // significand, which is 24 bits in `f32` and 53 bits in `f64`. - // https://github.com/rust-lang/rust/blob/58f11791af4f97572e7afd83f11cffe04bbbd12f/library/core/src/convert/num.rs#L151-L153 - Number::PosInt(v) => { - if v <= (1 << 53) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) - } - } - Number::NegInt(v) => { - if (-(1 << 53)..=(1 << 53)).contains(&v) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) - } - } - Number::Float(v) => Ok(v), - } - } -} - -/// Converts to an `f64`. The conversion fails if it is lossy. -impl TryFrom for f32 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => { - if v <= (1 << 24) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) - } - } - Number::NegInt(v) => { - if (-(1 << 24)..=(1 << 24)).contains(&v) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) - } - } - Number::Float(v) => Err(TryFromNumberErrorKind::F64ToF32LossyConversion(v).into()), - } - } -} - -#[cfg(test)] -mod number { - use super::*; - use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; - - macro_rules! to_unsigned_converter_tests { - ($typ:ident) => { - assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); - - assert!(matches!( - $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - assert!(matches!( - $typ::try_from(Number::NegInt(-1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - $typ::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - }; - } - - #[test] - fn to_u64() { - assert_eq!(u64::try_from(Number::PosInt(69u64)).unwrap(), 69u64); - - assert!(matches!( - u64::try_from(Number::NegInt(-1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - } - - #[test] - fn to_u32() { - to_unsigned_converter_tests!(u32); - } - - #[test] - fn to_u16() { - to_unsigned_converter_tests!(u16); - } - - #[test] - fn to_u8() { - to_unsigned_converter_tests!(u8); - } - - macro_rules! to_signed_converter_tests { - ($typ:ident) => { - assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); - assert_eq!($typ::try_from(Number::NegInt(-69i64)).unwrap(), -69); - - assert!(matches!( - $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - assert!(matches!( - $typ::try_from(Number::NegInt(($typ::MIN as i64) - 1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - }; - } - - #[test] - fn to_i64() { - assert_eq!(i64::try_from(Number::PosInt(69u64)).unwrap(), 69); - assert_eq!(i64::try_from(Number::NegInt(-69i64)).unwrap(), -69); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - } - - #[test] - fn to_i32() { - to_signed_converter_tests!(i32); - } - - #[test] - fn to_i16() { - to_signed_converter_tests!(i16); - } - - #[test] - fn to_i8() { - to_signed_converter_tests!(i8); - } - - #[test] - fn to_f64() { - assert_eq!(f64::try_from(Number::PosInt(69u64)).unwrap(), 69f64); - assert_eq!(f64::try_from(Number::NegInt(-69i64)).unwrap(), -69f64); - assert_eq!(f64::try_from(Number::Float(-69f64)).unwrap(), -69f64); - assert!(f64::try_from(Number::Float(f64::NAN)).unwrap().is_nan()); - assert_eq!( - f64::try_from(Number::Float(f64::INFINITY)).unwrap(), - f64::INFINITY - ); - assert_eq!( - f64::try_from(Number::Float(f64::NEG_INFINITY)).unwrap(), - f64::NEG_INFINITY - ); - - let significand_max_u64: u64 = 1 << 53; - let significand_max_i64: i64 = 1 << 53; - - assert_eq!( - f64::try_from(Number::PosInt(significand_max_u64)).unwrap(), - 9007199254740992f64 - ); - - assert_eq!( - f64::try_from(Number::NegInt(significand_max_i64)).unwrap(), - 9007199254740992f64 - ); - assert_eq!( - f64::try_from(Number::NegInt(-significand_max_i64)).unwrap(), - -9007199254740992f64 - ); - - assert!(matches!( - f64::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) - } - )); - - assert!(matches!( - f64::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - assert!(matches!( - f64::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - } - - #[test] - fn to_f32() { - assert_eq!(f32::try_from(Number::PosInt(69u64)).unwrap(), 69f32); - assert_eq!(f32::try_from(Number::NegInt(-69i64)).unwrap(), -69f32); - - let significand_max_u64: u64 = 1 << 24; - let significand_max_i64: i64 = 1 << 24; - - assert_eq!( - f32::try_from(Number::PosInt(significand_max_u64)).unwrap(), - 16777216f32 - ); - - assert_eq!( - f32::try_from(Number::NegInt(significand_max_i64)).unwrap(), - 16777216f32 - ); - assert_eq!( - f32::try_from(Number::NegInt(-significand_max_i64)).unwrap(), - -16777216f32 - ); - - assert!(matches!( - f32::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) - } - )); - - assert!(matches!( - f32::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - assert!(matches!( - f32::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - - for val in [69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - f32::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::F64ToF32LossyConversion(..) - } - )); - } - } - - #[test] - fn to_f64_lossy() { - assert_eq!(Number::PosInt(69u64).to_f64_lossy(), 69f64); - assert_eq!( - Number::PosInt((1 << 53) + 1).to_f64_lossy(), - 9007199254740992f64 - ); - assert_eq!( - Number::NegInt(-(1 << 53) - 1).to_f64_lossy(), - -9007199254740992f64 - ); - } - - #[test] - fn to_f32_lossy() { - assert_eq!(Number::PosInt(69u64).to_f32_lossy(), 69f32); - assert_eq!(Number::PosInt((1 << 24) + 1).to_f32_lossy(), 16777216f32); - assert_eq!(Number::NegInt(-(1 << 24) - 1).to_f32_lossy(), -16777216f32); - assert_eq!( - Number::Float(1452089033.7674935).to_f32_lossy(), - 1452089100f32 - ); - } -} +pub use number::Number; diff --git a/rust-runtime/aws-smithy-types/src/number.rs b/rust-runtime/aws-smithy-types/src/number.rs new file mode 100644 index 0000000000..85488b1299 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/number.rs @@ -0,0 +1,493 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! A number type that implements Javascript / JSON semantics. + +use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; +#[cfg(all( + aws_sdk_unstable, + any(feature = "serde-serialize", feature = "serde-deserialize") +))] +use serde; + +/// A number type that implements Javascript / JSON semantics, modeled on serde_json: +/// +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr( + all(aws_sdk_unstable, feature = "serde-deserialize"), + derive(serde::Deserialize) +)] +#[cfg_attr( + all(aws_sdk_unstable, feature = "serde-serialize"), + derive(serde::Serialize) +)] +#[cfg_attr( + any( + all(aws_sdk_unstable, feature = "serde-deserialize"), + all(aws_sdk_unstable, feature = "serde-serialize") + ), + serde(untagged) +)] +pub enum Number { + /// Unsigned 64-bit integer value. + PosInt(u64), + /// Signed 64-bit integer value. The wrapped value is _always_ negative. + NegInt(i64), + /// 64-bit floating-point value. + Float(f64), +} + +/* ANCHOR_END: document */ + +impl Number { + /// Converts to an `f64` lossily. + /// Use `Number::try_from` to make the conversion only if it is not lossy. + pub fn to_f64_lossy(self) -> f64 { + match self { + Number::PosInt(v) => v as f64, + Number::NegInt(v) => v as f64, + Number::Float(v) => v, + } + } + + /// Converts to an `f32` lossily. + /// Use `Number::try_from` to make the conversion only if it is not lossy. + pub fn to_f32_lossy(self) -> f32 { + match self { + Number::PosInt(v) => v as f32, + Number::NegInt(v) => v as f32, + Number::Float(v) => v as f32, + } + } +} + +macro_rules! to_unsigned_integer_converter { + ($typ:ident, $styp:expr) => { + #[doc = "Converts to a `"] + #[doc = $styp] + #[doc = "`. This conversion fails if it is lossy."] + impl TryFrom for $typ { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(Self::try_from(v)?), + Number::NegInt(v) => { + Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) + } + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } + } + }; + + ($typ:ident) => { + to_unsigned_integer_converter!($typ, stringify!($typ)); + }; +} + +macro_rules! to_signed_integer_converter { + ($typ:ident, $styp:expr) => { + #[doc = "Converts to a `"] + #[doc = $styp] + #[doc = "`. This conversion fails if it is lossy."] + impl TryFrom for $typ { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(Self::try_from(v)?), + Number::NegInt(v) => Ok(Self::try_from(v)?), + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } + } + }; + + ($typ:ident) => { + to_signed_integer_converter!($typ, stringify!($typ)); + }; +} + +/// Converts to a `u64`. The conversion fails if it is lossy. +impl TryFrom for u64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(v), + Number::NegInt(v) => { + Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) + } + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } +} +to_unsigned_integer_converter!(u32); +to_unsigned_integer_converter!(u16); +to_unsigned_integer_converter!(u8); + +impl TryFrom for i64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(Self::try_from(v)?), + Number::NegInt(v) => Ok(v), + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } +} +to_signed_integer_converter!(i32); +to_signed_integer_converter!(i16); +to_signed_integer_converter!(i8); + +/// Converts to an `f64`. The conversion fails if it is lossy. +impl TryFrom for f64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + // Integers can only be represented with full precision in a float if they fit in the + // significand, which is 24 bits in `f32` and 53 bits in `f64`. + // https://github.com/rust-lang/rust/blob/58f11791af4f97572e7afd83f11cffe04bbbd12f/library/core/src/convert/num.rs#L151-L153 + Number::PosInt(v) => { + if v <= (1 << 53) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) + } + } + Number::NegInt(v) => { + if (-(1 << 53)..=(1 << 53)).contains(&v) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) + } + } + Number::Float(v) => Ok(v), + } + } +} + +/// Converts to an `f64`. The conversion fails if it is lossy. +impl TryFrom for f32 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => { + if v <= (1 << 24) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) + } + } + Number::NegInt(v) => { + if (-(1 << 24)..=(1 << 24)).contains(&v) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) + } + } + Number::Float(v) => Err(TryFromNumberErrorKind::F64ToF32LossyConversion(v).into()), + } + } +} + +#[cfg(test)] +mod test { + use super::Number; + use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; + + macro_rules! to_unsigned_converter_tests { + ($typ:ident) => { + assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); + + assert!(matches!( + $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + assert!(matches!( + $typ::try_from(Number::NegInt(-1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + $typ::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + }; + } + + #[test] + fn to_u64() { + assert_eq!(u64::try_from(Number::PosInt(69u64)).unwrap(), 69u64); + + assert!(matches!( + u64::try_from(Number::NegInt(-1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + } + + #[test] + fn to_u32() { + to_unsigned_converter_tests!(u32); + } + + #[test] + fn to_u16() { + to_unsigned_converter_tests!(u16); + } + + #[test] + fn to_u8() { + to_unsigned_converter_tests!(u8); + } + + macro_rules! to_signed_converter_tests { + ($typ:ident) => { + assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); + assert_eq!($typ::try_from(Number::NegInt(-69i64)).unwrap(), -69); + + assert!(matches!( + $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + assert!(matches!( + $typ::try_from(Number::NegInt(($typ::MIN as i64) - 1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + }; + } + + #[test] + fn to_i64() { + assert_eq!(i64::try_from(Number::PosInt(69u64)).unwrap(), 69); + assert_eq!(i64::try_from(Number::NegInt(-69i64)).unwrap(), -69); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + } + + #[test] + fn to_i32() { + to_signed_converter_tests!(i32); + } + + #[test] + fn to_i16() { + to_signed_converter_tests!(i16); + } + + #[test] + fn to_i8() { + to_signed_converter_tests!(i8); + } + + #[test] + fn to_f64() { + assert_eq!(f64::try_from(Number::PosInt(69u64)).unwrap(), 69f64); + assert_eq!(f64::try_from(Number::NegInt(-69i64)).unwrap(), -69f64); + assert_eq!(f64::try_from(Number::Float(-69f64)).unwrap(), -69f64); + assert!(f64::try_from(Number::Float(f64::NAN)).unwrap().is_nan()); + assert_eq!( + f64::try_from(Number::Float(f64::INFINITY)).unwrap(), + f64::INFINITY + ); + assert_eq!( + f64::try_from(Number::Float(f64::NEG_INFINITY)).unwrap(), + f64::NEG_INFINITY + ); + + let significand_max_u64: u64 = 1 << 53; + let significand_max_i64: i64 = 1 << 53; + + assert_eq!( + f64::try_from(Number::PosInt(significand_max_u64)).unwrap(), + 9007199254740992f64 + ); + + assert_eq!( + f64::try_from(Number::NegInt(significand_max_i64)).unwrap(), + 9007199254740992f64 + ); + assert_eq!( + f64::try_from(Number::NegInt(-significand_max_i64)).unwrap(), + -9007199254740992f64 + ); + + assert!(matches!( + f64::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) + } + )); + + assert!(matches!( + f64::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + assert!(matches!( + f64::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + } + + #[test] + fn to_f32() { + assert_eq!(f32::try_from(Number::PosInt(69u64)).unwrap(), 69f32); + assert_eq!(f32::try_from(Number::NegInt(-69i64)).unwrap(), -69f32); + + let significand_max_u64: u64 = 1 << 24; + let significand_max_i64: i64 = 1 << 24; + + assert_eq!( + f32::try_from(Number::PosInt(significand_max_u64)).unwrap(), + 16777216f32 + ); + + assert_eq!( + f32::try_from(Number::NegInt(significand_max_i64)).unwrap(), + 16777216f32 + ); + assert_eq!( + f32::try_from(Number::NegInt(-significand_max_i64)).unwrap(), + -16777216f32 + ); + + assert!(matches!( + f32::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) + } + )); + + assert!(matches!( + f32::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + assert!(matches!( + f32::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + + for val in [69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + f32::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::F64ToF32LossyConversion(..) + } + )); + } + } + + #[test] + fn to_f64_lossy() { + assert_eq!(Number::PosInt(69u64).to_f64_lossy(), 69f64); + assert_eq!( + Number::PosInt((1 << 53) + 1).to_f64_lossy(), + 9007199254740992f64 + ); + assert_eq!( + Number::NegInt(-(1 << 53) - 1).to_f64_lossy(), + -9007199254740992f64 + ); + } + + #[test] + fn to_f32_lossy() { + assert_eq!(Number::PosInt(69u64).to_f32_lossy(), 69f32); + assert_eq!(Number::PosInt((1 << 24) + 1).to_f32_lossy(), 16777216f32); + assert_eq!(Number::NegInt(-(1 << 24) - 1).to_f32_lossy(), -16777216f32); + assert_eq!( + Number::Float(1452089033.7674935).to_f32_lossy(), + 1452089100f32 + ); + } + + #[test] + #[cfg(all( + test, + aws_sdk_unstable, + feature = "serde-deserialize", + feature = "serde-serialize" + ))] + /// ensures that numbers are deserialized as expected + /// 0 <= PosInt + /// 0 > NegInt + /// non integer values == Float + fn number_serde() { + let n: Number = serde_json::from_str("1.1").unwrap(); + assert_eq!(n, Number::Float(1.1)); + let n: Number = serde_json::from_str("1").unwrap(); + assert_eq!(n, Number::PosInt(1)); + let n: Number = serde_json::from_str("0").unwrap(); + assert_eq!(n, Number::PosInt(0)); + let n: Number = serde_json::from_str("-1").unwrap(); + assert_eq!(n, Number::NegInt(-1)); + + assert_eq!("1.1", serde_json::to_string(&Number::Float(1.1)).unwrap()); + assert_eq!("1", serde_json::to_string(&Number::PosInt(1)).unwrap()); + assert_eq!("0", serde_json::to_string(&Number::PosInt(0)).unwrap()); + assert_eq!("-1", serde_json::to_string(&Number::NegInt(-1)).unwrap()); + } +} diff --git a/rust-runtime/aws-smithy-types/src/retry.rs b/rust-runtime/aws-smithy-types/src/retry.rs index b96ababf06..a2866b84c1 100644 --- a/rust-runtime/aws-smithy-types/src/retry.rs +++ b/rust-runtime/aws-smithy-types/src/retry.rs @@ -5,6 +5,7 @@ //! This module defines types that describe when to retry given a response. +use crate::config_bag::{Storable, StoreReplace}; use std::fmt; use std::str::FromStr; use std::time::Duration; @@ -98,12 +99,12 @@ impl FromStr for RetryMode { fn from_str(string: &str) -> Result { let string = string.trim(); + // eq_ignore_ascii_case is OK here because the only strings we need to check for are ASCII if string.eq_ignore_ascii_case("standard") { Ok(RetryMode::Standard) - // TODO(https://github.com/awslabs/aws-sdk-rust/issues/247): adaptive retries - // } else if string.eq_ignore_ascii_case("adaptive") { - // Ok(RetryMode::Adaptive) + } else if string.eq_ignore_ascii_case("adaptive") { + Ok(RetryMode::Adaptive) } else { Err(RetryModeParseError::new(string)) } @@ -143,6 +144,7 @@ pub struct RetryConfigBuilder { mode: Option, max_attempts: Option, initial_backoff: Option, + max_backoff: Option, reconnect_mode: Option, } @@ -212,6 +214,18 @@ impl RetryConfigBuilder { self } + /// Set the max_backoff duration. This duration should be non-zero. + pub fn set_max_backoff(&mut self, max_backoff: Option) -> &mut Self { + self.max_backoff = max_backoff; + self + } + + /// Set the max_backoff duration. This duration should be non-zero. + pub fn max_backoff(mut self, max_backoff: Duration) -> Self { + self.set_max_backoff(Some(max_backoff)); + self + } + /// Merge two builders together. Values from `other` will only be used as a fallback for values /// from `self` Useful for merging configs from different sources together when you want to /// handle "precedence" per value instead of at the config level @@ -233,6 +247,7 @@ impl RetryConfigBuilder { mode: self.mode.or(other.mode), max_attempts: self.max_attempts.or(other.max_attempts), initial_backoff: self.initial_backoff.or(other.initial_backoff), + max_backoff: self.max_backoff.or(other.max_backoff), reconnect_mode: self.reconnect_mode.or(other.reconnect_mode), } } @@ -248,6 +263,8 @@ impl RetryConfigBuilder { reconnect_mode: self .reconnect_mode .unwrap_or(ReconnectMode::ReconnectOnTransientError), + max_backoff: self.max_backoff.unwrap_or_else(|| Duration::from_secs(20)), + use_static_exponential_base: false, } } } @@ -259,7 +276,13 @@ pub struct RetryConfig { mode: RetryMode, max_attempts: u32, initial_backoff: Duration, + max_backoff: Duration, reconnect_mode: ReconnectMode, + use_static_exponential_base: bool, +} + +impl Storable for RetryConfig { + type Storer = StoreReplace; } /// Mode for connection re-establishment @@ -278,6 +301,10 @@ pub enum ReconnectMode { ReuseAllConnections, } +impl Storable for ReconnectMode { + type Storer = StoreReplace; +} + impl RetryConfig { /// Creates a default `RetryConfig` with `RetryMode::Standard` and max attempts of three. pub fn standard() -> Self { @@ -286,6 +313,20 @@ impl RetryConfig { max_attempts: 3, initial_backoff: Duration::from_secs(1), reconnect_mode: ReconnectMode::ReconnectOnTransientError, + max_backoff: Duration::from_secs(20), + use_static_exponential_base: false, + } + } + + /// Creates a default `RetryConfig` with `RetryMode::Adaptive` and max attempts of three. + pub fn adaptive() -> Self { + Self { + mode: RetryMode::Adaptive, + max_attempts: 3, + initial_backoff: Duration::from_secs(1), + reconnect_mode: ReconnectMode::ReconnectOnTransientError, + max_backoff: Duration::from_secs(20), + use_static_exponential_base: false, } } @@ -341,6 +382,20 @@ impl RetryConfig { self } + /// Hint to the retry strategy whether to use a static exponential base. + /// + /// When a retry strategy uses exponential backoff, it calculates a random base. This causes the + /// retry delay to be slightly random, and helps prevent "thundering herd" scenarios. However, + /// it's often useful during testing to know exactly how long the delay will be. + /// + /// Therefore, if you're writing a test and asserting an expected retry delay, + /// set this to `true`. + #[cfg(feature = "test-util")] + pub fn with_use_static_exponential_base(mut self, use_static_exponential_base: bool) -> Self { + self.use_static_exponential_base = use_static_exponential_base; + self + } + /// Returns the retry mode. pub fn mode(&self) -> RetryMode { self.mode @@ -361,10 +416,23 @@ impl RetryConfig { self.initial_backoff } + /// Returns the max backoff duration. + pub fn max_backoff(&self) -> Duration { + self.max_backoff + } + /// Returns true if retry is enabled with this config pub fn has_retry(&self) -> bool { self.max_attempts > 1 } + + /// Returns `true` if retry strategies should use a static exponential base instead of the + /// default random base. + /// + /// To set this value, the `test-util` feature must be enabled. + pub fn use_static_exponential_base(&self) -> bool { + self.use_static_exponential_base + } } #[cfg(test)] diff --git a/rust-runtime/aws-smithy-types/src/timeout.rs b/rust-runtime/aws-smithy-types/src/timeout.rs index 04e92650a9..160895ceb1 100644 --- a/rust-runtime/aws-smithy-types/src/timeout.rs +++ b/rust-runtime/aws-smithy-types/src/timeout.rs @@ -6,6 +6,7 @@ //! This module defines types that describe timeouts that can be applied to various stages of the //! Smithy networking stack. +use crate::config_bag::{Storable, StoreReplace}; use std::time::Duration; /// Builder for [`TimeoutConfig`]. @@ -208,6 +209,10 @@ pub struct TimeoutConfig { operation_attempt_timeout: Option, } +impl Storable for TimeoutConfig { + type Storer = StoreReplace; +} + impl TimeoutConfig { /// Returns a builder to create a `TimeoutConfig`. pub fn builder() -> TimeoutConfigBuilder { diff --git a/rust-runtime/aws-smithy-types/src/type_erasure.rs b/rust-runtime/aws-smithy-types/src/type_erasure.rs new file mode 100644 index 0000000000..86533cb9f3 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/type_erasure.rs @@ -0,0 +1,290 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::any::Any; +use std::error::Error as StdError; +use std::fmt; +use std::sync::Arc; + +/// Abstraction over `Box` that provides `Debug` and optionally `Clone`. +/// +/// The orchestrator uses `TypeErasedBox` to avoid the complication of six or more generic parameters +/// and to avoid the monomorphization that brings with it. +/// +/// # Examples +/// +/// Creating a box: +/// ```no_run +/// use aws_smithy_types::type_erasure::TypeErasedBox; +/// +/// let boxed = TypeErasedBox::new("some value"); +/// ``` +/// +/// Downcasting a box: +/// ```no_run +/// # use aws_smithy_types::type_erasure::TypeErasedBox; +/// # let boxed = TypeErasedBox::new("some value".to_string()); +/// let value: Option<&String> = boxed.downcast_ref::(); +/// ``` +/// +/// Converting a box back into its value: +/// ``` +/// # use aws_smithy_types::type_erasure::TypeErasedBox; +/// let boxed = TypeErasedBox::new("some value".to_string()); +/// let value: String = *boxed.downcast::().expect("it is a string"); +/// ``` +pub struct TypeErasedBox { + field: Box, + #[allow(clippy::type_complexity)] + debug: Arc< + dyn Fn(&Box, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, + >, + #[allow(clippy::type_complexity)] + clone: Option) -> TypeErasedBox + Send + Sync>>, +} + +#[cfg(feature = "test-util")] +impl TypeErasedBox { + /// Often, when testing the orchestrator or its components, it's necessary to provide a + /// `TypeErasedBox` to serve as an `Input` for `invoke`. In cases where the type won't actually + /// be accessed during testing, use this method to generate a `TypeErasedBox` that makes it + /// clear that "for the purpose of this test, the `Input` doesn't matter." + pub fn doesnt_matter() -> Self { + Self::new("doesn't matter") + } +} + +impl fmt::Debug for TypeErasedBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TypeErasedBox[")?; + if self.clone.is_some() { + f.write_str("Clone")?; + } else { + f.write_str("!Clone")?; + } + f.write_str("]:")?; + (self.debug)(&self.field, f) + } +} + +impl TypeErasedBox { + /// Create a new `TypeErasedBox` from `value` of type `T` + pub fn new(value: T) -> Self { + let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { + fmt::Debug::fmt(value.downcast_ref::().expect("type-checked"), f) + }; + Self { + field: Box::new(value), + debug: Arc::new(debug), + clone: None, + } + } + + /// Create a new cloneable `TypeErasedBox` from the given `value`. + pub fn new_with_clone(value: T) -> Self { + let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { + fmt::Debug::fmt(value.downcast_ref::().expect("type-checked"), f) + }; + let clone = |value: &Box| { + TypeErasedBox::new_with_clone(value.downcast_ref::().expect("typechecked").clone()) + }; + Self { + field: Box::new(value), + debug: Arc::new(debug), + clone: Some(Arc::new(clone)), + } + } + + /// Attempts to clone this box. + /// + /// Note: this will only ever succeed if the box was created with [`TypeErasedBox::new_with_clone`]. + pub fn try_clone(&self) -> Option { + Some((self.clone.as_ref()?)(&self.field)) + } + + /// Downcast into a `Box`, or return `Self` if it is not a `T`. + pub fn downcast(self) -> Result, Self> { + let TypeErasedBox { + field, + debug, + clone, + } = self; + field.downcast().map_err(|field| Self { + field, + debug, + clone, + }) + } + + /// Downcast as a `&T`, or return `None` if it is not a `T`. + pub fn downcast_ref(&self) -> Option<&T> { + self.field.downcast_ref() + } + + /// Downcast as a `&mut T`, or return `None` if it is not a `T`. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.field.downcast_mut() + } +} + +impl From for TypeErasedBox { + fn from(value: TypeErasedError) -> Self { + TypeErasedBox { + field: value.field, + debug: value.debug, + clone: None, + } + } +} + +/// A new-type around `Box` that also implements `Error` +pub struct TypeErasedError { + field: Box, + #[allow(clippy::type_complexity)] + debug: Arc< + dyn Fn(&Box, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, + >, + #[allow(clippy::type_complexity)] + as_error: Box Fn(&'a TypeErasedError) -> &'a (dyn StdError) + Send + Sync>, +} + +impl fmt::Debug for TypeErasedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TypeErasedError:")?; + (self.debug)(&self.field, f) + } +} + +impl fmt::Display for TypeErasedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt((self.as_error)(self), f) + } +} + +impl StdError for TypeErasedError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + (self.as_error)(self).source() + } +} + +impl TypeErasedError { + /// Create a new `TypeErasedError` from `value` of type `T` + pub fn new(value: T) -> Self { + let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { + fmt::Debug::fmt(value.downcast_ref::().expect("typechecked"), f) + }; + Self { + field: Box::new(value), + debug: Arc::new(debug), + as_error: Box::new(|value: &TypeErasedError| { + value.downcast_ref::().expect("typechecked") as _ + }), + } + } + + /// Downcast into a `Box`, or return `Self` if it is not a `T`. + pub fn downcast( + self, + ) -> Result, Self> { + let TypeErasedError { + field, + debug, + as_error, + } = self; + field.downcast().map_err(|field| Self { + field, + debug, + as_error, + }) + } + + /// Downcast as a `&T`, or return `None` if it is not a `T`. + pub fn downcast_ref(&self) -> Option<&T> { + self.field.downcast_ref() + } + + /// Downcast as a `&mut T`, or return `None` if it is not a `T`. + pub fn downcast_mut( + &mut self, + ) -> Option<&mut T> { + self.field.downcast_mut() + } + + /// Returns a `TypeErasedError` with a fake/test value with the expectation that it won't be downcast in the test. + #[cfg(feature = "test-util")] + pub fn doesnt_matter() -> Self { + #[derive(Debug)] + struct FakeError; + impl fmt::Display for FakeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "FakeError") + } + } + impl StdError for FakeError {} + Self::new(FakeError) + } +} + +#[cfg(test)] +mod tests { + use super::{TypeErasedBox, TypeErasedError}; + use std::fmt; + + #[derive(Debug)] + struct Foo(&'static str); + #[derive(Debug)] + struct Bar(isize); + + #[derive(Debug, Clone, PartialEq, Eq)] + struct TestErr { + inner: &'static str, + } + + impl TestErr { + fn new(inner: &'static str) -> Self { + Self { inner } + } + } + + impl fmt::Display for TestErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error: {}", self.inner) + } + } + + impl std::error::Error for TestErr {} + + #[test] + fn test_typed_erased_errors_can_be_downcast() { + let test_err = TestErr::new("something failed!"); + let type_erased_test_err = TypeErasedError::new(test_err.clone()); + let actual = type_erased_test_err + .downcast::() + .expect("type erased error can be downcast into original type"); + assert_eq!(test_err, *actual); + } + + #[test] + fn test_typed_erased_errors_can_be_unwrapped() { + let test_err = TestErr::new("something failed!"); + let type_erased_test_err = TypeErasedError::new(test_err.clone()); + let actual = *type_erased_test_err + .downcast::() + .expect("type erased error can be downcast into original type"); + assert_eq!(test_err, actual); + } + + #[test] + fn test_typed_cloneable_boxes() { + let expected_str = "I can be cloned"; + let cloneable = TypeErasedBox::new_with_clone(expected_str.to_owned()); + // ensure it can be cloned + let cloned = cloneable.try_clone().unwrap(); + let actual_str = cloned.downcast_ref::().unwrap(); + assert_eq!(expected_str, actual_str); + // they should point to different addresses + assert_ne!(format!("{expected_str:p}"), format! {"{actual_str:p}"}); + } +} diff --git a/rust-runtime/aws-smithy-types/test_data/serialize_document.json b/rust-runtime/aws-smithy-types/test_data/serialize_document.json new file mode 100644 index 0000000000..dcbac2986a --- /dev/null +++ b/rust-runtime/aws-smithy-types/test_data/serialize_document.json @@ -0,0 +1,43 @@ +{ + "null": null, + "true": true, + "pos_int": 1, + "false": false, + "map": { + "array": [ + { + "pos_int": 1, + "float": 0.30000000000000004, + "neg_int": -1, + "hello": "world", + "false": false, + "true": true + }, + "hello-world", + true, + false + ], + "pos_int": 1, + "float": 0.30000000000000004, + "neg_int": -1, + "hello": "world", + "false": false, + "true": true + }, + "float": 0.30000000000000004, + "neg_int": -1, + "hello": "world", + "array": [ + { + "pos_int": 1, + "float": 0.30000000000000004, + "neg_int": -1, + "hello": "world", + "false": false, + "true": true + }, + "hello-world", + true, + false + ] +} diff --git a/rust-runtime/aws-smithy-xml/external-types.toml b/rust-runtime/aws-smithy-xml/external-types.toml index b4b49878e5..ff30ccf5ad 100644 --- a/rust-runtime/aws-smithy-xml/external-types.toml +++ b/rust-runtime/aws-smithy-xml/external-types.toml @@ -1,5 +1,2 @@ allowed_external_types = [ - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Don't expose `xmlparser` at all - "xmlparser::Token", - "xmlparser::error::Error", ] diff --git a/rust-runtime/aws-smithy-xml/src/decode.rs b/rust-runtime/aws-smithy-xml/src/decode.rs index 522c5b12e3..a81c9dbe5f 100644 --- a/rust-runtime/aws-smithy-xml/src/decode.rs +++ b/rust-runtime/aws-smithy-xml/src/decode.rs @@ -28,14 +28,6 @@ pub struct XmlDecodeError { kind: XmlDecodeErrorKind, } -impl From for XmlDecodeError { - fn from(error: xmlparser::Error) -> Self { - Self { - kind: XmlDecodeErrorKind::InvalidXml(error), - } - } -} - impl Display for XmlDecodeError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match &self.kind { @@ -58,6 +50,12 @@ impl Error for XmlDecodeError { } impl XmlDecodeError { + pub(crate) fn invalid_xml(error: xmlparser::Error) -> Self { + Self { + kind: XmlDecodeErrorKind::InvalidXml(error), + } + } + pub(crate) fn invalid_escape(esc: impl Into) -> Self { Self { kind: XmlDecodeErrorKind::InvalidEscape { esc: esc.into() }, @@ -256,6 +254,11 @@ impl<'inp> Document<'inp> { } } +/// A new-type wrapper around `Token` to prevent the wrapped third party type from showing up in +/// public API +#[derive(Debug)] +pub struct XmlToken<'inp>(Token<'inp>); + /// Depth tracking iterator /// /// ```xml @@ -267,11 +270,11 @@ impl<'inp> Document<'inp> { /// <- endel depth 0 /// ``` impl<'inp> Iterator for Document<'inp> { - type Item = Result<(Token<'inp>, Depth), XmlDecodeError>; - fn next<'a>(&'a mut self) -> Option, Depth), XmlDecodeError>> { + type Item = Result<(XmlToken<'inp>, Depth), XmlDecodeError>; + fn next<'a>(&'a mut self) -> Option, Depth), XmlDecodeError>> { let tok = self.tokenizer.next()?; let tok = match tok { - Err(e) => return Some(Err(e.into())), + Err(e) => return Some(Err(XmlDecodeError::invalid_xml(e))), Ok(tok) => tok, }; // depth bookkeeping @@ -290,11 +293,11 @@ impl<'inp> Iterator for Document<'inp> { self.depth += 1; // We want the startel and endel to have the same depth, but after the opener, // the parser will be at depth 1. Return the previous depth: - return Some(Ok((t, self.depth - 1))); + return Some(Ok((XmlToken(t), self.depth - 1))); } _ => {} } - Some(Ok((tok, self.depth))) + Some(Ok((XmlToken(tok), self.depth))) } } @@ -351,7 +354,7 @@ impl<'inp> ScopedDecoder<'inp, '_> { } impl<'inp, 'a> Iterator for ScopedDecoder<'inp, 'a> { - type Item = Result<(Token<'inp>, Depth), XmlDecodeError>; + type Item = Result<(XmlToken<'inp>, Depth), XmlDecodeError>; fn next(&mut self) -> Option { if self.start_el.closed { @@ -365,7 +368,7 @@ impl<'inp, 'a> Iterator for ScopedDecoder<'inp, 'a> { other => return other, }; - match tok { + match tok.0 { Token::ElementEnd { end, .. } if self.start_el.end_el(end, depth) => { self.terminated = true; return None; @@ -378,23 +381,23 @@ impl<'inp, 'a> Iterator for ScopedDecoder<'inp, 'a> { /// Load the next start element out of a depth-tagged token iterator fn next_start_element<'a, 'inp>( - tokens: &'a mut impl Iterator, Depth), XmlDecodeError>>, + tokens: &'a mut impl Iterator, Depth), XmlDecodeError>>, ) -> Option> { let mut out = StartEl::new("", "", 0); loop { match tokens.next()? { - Ok((Token::ElementStart { local, prefix, .. }, depth)) => { + Ok((XmlToken(Token::ElementStart { local, prefix, .. }), depth)) => { out.name.local = local.as_str(); out.name.prefix = prefix.as_str(); out.depth = depth; } Ok(( - Token::Attribute { + XmlToken(Token::Attribute { prefix, local, value, .. - }, + }), _, )) => out.attributes.push(Attr { name: Name { @@ -404,17 +407,17 @@ fn next_start_element<'a, 'inp>( value: unescape(value.as_str()).ok()?, }), Ok(( - Token::ElementEnd { + XmlToken(Token::ElementEnd { end: ElementEnd::Open, .. - }, + }), _, )) => break, Ok(( - Token::ElementEnd { + XmlToken(Token::ElementEnd { end: ElementEnd::Empty, .. - }, + }), _, )) => { out.closed = true; @@ -431,13 +434,13 @@ fn next_start_element<'a, 'inp>( /// If the current position is not a data element (and is instead a ``) an error /// will be returned pub fn try_data<'a, 'inp>( - tokens: &'a mut impl Iterator, Depth), XmlDecodeError>>, + tokens: &'a mut impl Iterator, Depth), XmlDecodeError>>, ) -> Result, XmlDecodeError> { loop { match tokens.next().map(|opt| opt.map(|opt| opt.0)) { None => return Ok(Cow::Borrowed("")), - Some(Ok(Token::Text { text })) => return unescape(text.as_str()), - Some(Ok(e @ Token::ElementStart { .. })) => { + Some(Ok(XmlToken(Token::Text { text }))) => return unescape(text.as_str()), + Some(Ok(e @ XmlToken(Token::ElementStart { .. }))) => { return Err(XmlDecodeError::custom(format!( "looking for a data element, found: {:?}", e diff --git a/rust-runtime/clippy.toml b/rust-runtime/clippy.toml new file mode 120000 index 0000000000..939baf7183 --- /dev/null +++ b/rust-runtime/clippy.toml @@ -0,0 +1 @@ +../clippy-root.toml \ No newline at end of file diff --git a/rust-runtime/inlineable/Cargo.toml b/rust-runtime/inlineable/Cargo.toml index b76a17e5e7..6d0c099429 100644 --- a/rust-runtime/inlineable/Cargo.toml +++ b/rust-runtime/inlineable/Cargo.toml @@ -18,22 +18,24 @@ default = ["gated-tests"] [dependencies] -"bytes" = "1" -"http" = "0.2.1" -"aws-smithy-types" = { path = "../aws-smithy-types" } -"aws-smithy-json" = { path = "../aws-smithy-json" } -"aws-smithy-xml" = { path = "../aws-smithy-xml" } -"aws-smithy-http-server" = { path = "../aws-smithy-http-server" } -"fastrand" = "1" -"futures-util" = "0.3" -"pin-project-lite" = "0.2" -"tower" = { version = "0.4.11", default_features = false } -"async-trait" = "0.1" -percent-encoding = "2.2.0" +async-trait = "0.1" +aws-smithy-http = { path = "../aws-smithy-http" } +aws-smithy-http-server = { path = "../aws-smithy-http-server" } +aws-smithy-json = { path = "../aws-smithy-json" } +aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["client"] } +aws-smithy-types = { path = "../aws-smithy-types" } +aws-smithy-xml = { path = "../aws-smithy-xml" } +bytes = "1" +fastrand = "2.0.0" +futures-util = "0.3" +http = "0.2.1" +md-5 = "0.10.0" once_cell = "1.16.0" +percent-encoding = "2.2.0" +pin-project-lite = "0.2" regex = "1.5.5" -"url" = "2.2.2" -aws-smithy-http = { path = "../aws-smithy-http" } +tower = { version = "0.4.11", default-features = false } +url = "2.2.2" [dev-dependencies] proptest = "1" diff --git a/rust-runtime/inlineable/src/client_http_checksum_required.rs b/rust-runtime/inlineable/src/client_http_checksum_required.rs new file mode 100644 index 0000000000..10c129b3bf --- /dev/null +++ b/rust-runtime/inlineable/src/client_http_checksum_required.rs @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::{Interceptor, SharedInterceptor}; +use aws_smithy_runtime_api::client::runtime_components::{ + RuntimeComponents, RuntimeComponentsBuilder, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::base64; +use aws_smithy_types::config_bag::ConfigBag; +use http::header::HeaderName; +use std::borrow::Cow; + +#[derive(Debug)] +pub(crate) struct HttpChecksumRequiredRuntimePlugin { + runtime_components: RuntimeComponentsBuilder, +} + +impl HttpChecksumRequiredRuntimePlugin { + pub(crate) fn new() -> Self { + Self { + runtime_components: RuntimeComponentsBuilder::new("HttpChecksumRequiredRuntimePlugin") + .with_interceptor(SharedInterceptor::new(HttpChecksumRequiredInterceptor)), + } + } +} + +impl RuntimePlugin for HttpChecksumRequiredRuntimePlugin { + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.runtime_components) + } +} + +#[derive(Debug)] +struct HttpChecksumRequiredInterceptor; + +impl Interceptor for HttpChecksumRequiredInterceptor { + fn name(&self) -> &'static str { + "HttpChecksumRequiredInterceptor" + } + + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut(); + let body_bytes = request + .body() + .bytes() + .expect("checksum can only be computed for non-streaming operations"); + let checksum = ::digest(body_bytes); + request.headers_mut().insert( + HeaderName::from_static("content-md5"), + base64::encode(&checksum[..]) + .parse() + .expect("checksum is a valid header value"), + ); + Ok(()) + } +} diff --git a/rust-runtime/inlineable/src/client_idempotency_token.rs b/rust-runtime/inlineable/src/client_idempotency_token.rs new file mode 100644 index 0000000000..778fc1133b --- /dev/null +++ b/rust-runtime/inlineable/src/client_idempotency_token.rs @@ -0,0 +1,76 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::idempotency_token::IdempotencyTokenProvider; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextMut, Input, +}; +use aws_smithy_runtime_api::client::interceptors::{Interceptor, SharedInterceptor}; +use aws_smithy_runtime_api::client::runtime_components::{ + RuntimeComponents, RuntimeComponentsBuilder, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::ConfigBag; +use std::borrow::Cow; +use std::fmt; + +#[derive(Debug)] +pub(crate) struct IdempotencyTokenRuntimePlugin { + runtime_components: RuntimeComponentsBuilder, +} + +impl IdempotencyTokenRuntimePlugin { + pub(crate) fn new(set_token: S) -> Self + where + S: Fn(IdempotencyTokenProvider, &mut Input) + Send + Sync + 'static, + { + Self { + runtime_components: RuntimeComponentsBuilder::new("IdempotencyTokenRuntimePlugin") + .with_interceptor(SharedInterceptor::new(IdempotencyTokenInterceptor { + set_token, + })), + } + } +} + +impl RuntimePlugin for IdempotencyTokenRuntimePlugin { + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.runtime_components) + } +} + +struct IdempotencyTokenInterceptor { + set_token: S, +} + +impl fmt::Debug for IdempotencyTokenInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IdempotencyTokenInterceptor").finish() + } +} + +impl Interceptor for IdempotencyTokenInterceptor +where + S: Fn(IdempotencyTokenProvider, &mut Input) + Send + Sync, +{ + fn name(&self) -> &'static str { + "IdempotencyTokenInterceptor" + } + + fn modify_before_serialization( + &self, + context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let token_provider = cfg + .load::() + .expect("the idempotency provider must be set") + .clone(); + (self.set_token)(token_provider, context.input_mut()); + Ok(()) + } +} diff --git a/rust-runtime/inlineable/src/endpoint_lib/arn.rs b/rust-runtime/inlineable/src/endpoint_lib/arn.rs index a9b599e632..0fd68d73bc 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/arn.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/arn.rs @@ -89,7 +89,7 @@ impl<'a> Arn<'a> { } } -pub(crate) fn parse_arn<'a, 'b>(input: &'a str, e: &'b mut DiagnosticCollector) -> Option> { +pub(crate) fn parse_arn<'a>(input: &'a str, e: &mut DiagnosticCollector) -> Option> { e.capture(Arn::parse(input)) } diff --git a/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs b/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs index 5e437c9bfe..03e07f3e41 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs @@ -45,7 +45,7 @@ impl<'a> Url<'a> { } } -pub(crate) fn parse_url<'a, 'b>(url: &'a str, e: &'b mut DiagnosticCollector) -> Option> { +pub(crate) fn parse_url<'a>(url: &'a str, e: &mut DiagnosticCollector) -> Option> { let raw = url; let uri: Uri = e.capture(url.parse())?; let url: ParsedUrl = e.capture(url.parse())?; diff --git a/rust-runtime/inlineable/src/endpoint_lib/partition.rs b/rust-runtime/inlineable/src/endpoint_lib/partition.rs index 55b97a21d2..02088d0f93 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/partition.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/partition.rs @@ -17,7 +17,7 @@ use std::borrow::Cow; use std::collections::HashMap; /// Determine the AWS partition metadata for a given region -#[derive(Default)] +#[derive(Debug, Default)] pub(crate) struct PartitionResolver { partitions: Vec, } @@ -151,6 +151,7 @@ impl PartitionResolver { type Str = Cow<'static, str>; +#[derive(Debug)] pub(crate) struct PartitionMetadata { id: Str, region_regex: Regex, @@ -203,6 +204,7 @@ impl PartitionMetadata { } } +#[derive(Debug)] pub(crate) struct PartitionOutput { name: Str, dns_suffix: Str, @@ -211,7 +213,7 @@ pub(crate) struct PartitionOutput { supports_dual_stack: bool, } -#[derive(Default)] +#[derive(Debug, Default)] pub(crate) struct PartitionOutputOverride { name: Option, dns_suffix: Option, @@ -456,7 +458,7 @@ mod test { use regex::Regex; use std::collections::HashMap; - fn resolve<'a, 'b>(resolver: &'a PartitionResolver, region: &'b str) -> Partition<'a> { + fn resolve<'a>(resolver: &'a PartitionResolver, region: &str) -> Partition<'a> { resolver .resolve_partition(region, &mut DiagnosticCollector::new()) .expect("could not resolve partition") diff --git a/rust-runtime/inlineable/src/endpoint_lib/substring.rs b/rust-runtime/inlineable/src/endpoint_lib/substring.rs index 8512810c4e..27142e118e 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/substring.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/substring.rs @@ -13,12 +13,12 @@ use crate::endpoint_lib::diagnostic::DiagnosticCollector; /// - When `reverse` is false, indexes are evaluated from the beginning of the string /// - When `reverse` is true, indexes are evaluated from the end of the string (however, the result /// will still be "forwards" and `start` MUST be less than `end`. -pub(crate) fn substring<'a, 'b>( +pub(crate) fn substring<'a>( input: &'a str, start: usize, stop: usize, reverse: bool, - e: &'b mut DiagnosticCollector, + e: &mut DiagnosticCollector, ) -> Option<&'a str> { if start >= stop { e.capture(Err("start > stop"))?; diff --git a/rust-runtime/inlineable/src/endpoint_lib/uri_encode.rs b/rust-runtime/inlineable/src/endpoint_lib/uri_encode.rs index 58c0ef5742..de33264a9f 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/uri_encode.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/uri_encode.rs @@ -39,9 +39,9 @@ pub(crate) const BASE_SET: &AsciiSet = &CONTROLS .add(b'\\'); // Returns `Option` for forwards compatibility -pub(crate) fn uri_encode<'a, 'b>( +pub(crate) fn uri_encode<'a>( s: &'a str, - _e: &'b mut DiagnosticCollector, + _e: &mut DiagnosticCollector, ) -> std::borrow::Cow<'a, str> { utf8_percent_encode(s, BASE_SET).into() } diff --git a/rust-runtime/inlineable/src/http_checksum_required.rs b/rust-runtime/inlineable/src/http_checksum_required.rs new file mode 100644 index 0000000000..636016a3fc --- /dev/null +++ b/rust-runtime/inlineable/src/http_checksum_required.rs @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorRegistrar, SharedInterceptor, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::base64; +use aws_smithy_types::config_bag::ConfigBag; +use http::header::HeaderName; + +#[derive(Debug)] +pub(crate) struct HttpChecksumRequiredRuntimePlugin; + +impl RuntimePlugin for HttpChecksumRequiredRuntimePlugin { + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { + interceptors.register(SharedInterceptor::new(HttpChecksumRequiredInterceptor)); + } +} + +#[derive(Debug)] +struct HttpChecksumRequiredInterceptor; + +impl Interceptor for HttpChecksumRequiredInterceptor { + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut(); + let body_bytes = request + .body() + .bytes() + .expect("checksum can only be computed for non-streaming operations"); + let checksum = ::digest(body_bytes); + request.headers_mut().insert( + HeaderName::from_static("content-md5"), + base64::encode(&checksum[..]) + .parse() + .expect("checksum is a valid header value"), + ); + Ok(()) + } +} diff --git a/rust-runtime/inlineable/src/idempotency_token.rs b/rust-runtime/inlineable/src/idempotency_token.rs index 9d55f46db5..12921f02af 100644 --- a/rust-runtime/inlineable/src/idempotency_token.rs +++ b/rust-runtime/inlineable/src/idempotency_token.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::sync::Mutex; pub(crate) fn uuid_v4(input: u128) -> String { @@ -37,10 +38,12 @@ pub(crate) fn uuid_v4(input: u128) -> String { /// for testing, two options are available: /// 1. Utilize the From<&'static str>` implementation to hard code an idempotency token /// 2. Seed the token provider with [`IdempotencyTokenProvider::with_seed`](IdempotencyTokenProvider::with_seed) +#[derive(Debug)] pub struct IdempotencyTokenProvider { inner: Inner, } +#[derive(Debug)] enum Inner { Static(&'static str), Random(Mutex), @@ -56,6 +59,10 @@ impl From<&'static str> for IdempotencyTokenProvider { } } +impl Storable for IdempotencyTokenProvider { + type Storer = StoreReplace; +} + impl IdempotencyTokenProvider { pub fn make_idempotency_token(&self) -> String { match &self.inner { diff --git a/rust-runtime/inlineable/src/lib.rs b/rust-runtime/inlineable/src/lib.rs index e53b81db7d..b672eeef9d 100644 --- a/rust-runtime/inlineable/src/lib.rs +++ b/rust-runtime/inlineable/src/lib.rs @@ -6,6 +6,10 @@ #[allow(dead_code)] mod aws_query_compatible_errors; #[allow(unused)] +mod client_http_checksum_required; +#[allow(dead_code)] +mod client_idempotency_token; +#[allow(unused)] mod constrained; #[allow(dead_code)] mod ec2_query_errors; @@ -17,6 +21,8 @@ mod json_errors; mod rest_xml_unwrapped_errors; #[allow(unused)] mod rest_xml_wrapped_errors; +#[allow(unused)] +mod serialization_settings; #[allow(unused)] mod endpoint_lib; diff --git a/rust-runtime/inlineable/src/serialization_settings.rs b/rust-runtime/inlineable/src/serialization_settings.rs new file mode 100644 index 0000000000..a00e38e6b4 --- /dev/null +++ b/rust-runtime/inlineable/src/serialization_settings.rs @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +use aws_smithy_http::header::set_request_header_if_absent; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; +use http::header::{HeaderName, CONTENT_LENGTH, CONTENT_TYPE}; + +/// Configuration for how default protocol headers are serialized +#[derive(Clone, Debug, Default)] +pub(crate) struct HeaderSerializationSettings { + omit_default_content_length: bool, + omit_default_content_type: bool, +} + +impl HeaderSerializationSettings { + /// Creates new [`HeaderSerializationSettings`] + pub(crate) fn new() -> Self { + Default::default() + } + + /// Omit the default `Content-Length` header during serialization + pub(crate) fn omit_default_content_length(self) -> Self { + Self { + omit_default_content_length: true, + ..self + } + } + + /// Omit the default `Content-Type` header during serialization + pub(crate) fn omit_default_content_type(self) -> Self { + Self { + omit_default_content_type: true, + ..self + } + } + + /// Returns true if the given default header name should be serialized + fn include_header(&self, header: &HeaderName) -> bool { + (!self.omit_default_content_length || header != CONTENT_LENGTH) + && (!self.omit_default_content_type || header != CONTENT_TYPE) + } + + /// Sets a default header on the given request builder if it should be serialized + pub(crate) fn set_default_header( + &self, + mut request: http::request::Builder, + header_name: HeaderName, + value: &str, + ) -> http::request::Builder { + if self.include_header(&header_name) { + request = set_request_header_if_absent(request, header_name, value); + } + request + } +} + +impl Storable for HeaderSerializationSettings { + type Storer = StoreReplace; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_include_header() { + let settings = HeaderSerializationSettings::default(); + assert!(settings.include_header(&CONTENT_LENGTH)); + assert!(settings.include_header(&CONTENT_TYPE)); + + let settings = HeaderSerializationSettings::default().omit_default_content_length(); + assert!(!settings.include_header(&CONTENT_LENGTH)); + assert!(settings.include_header(&CONTENT_TYPE)); + + let settings = HeaderSerializationSettings::default().omit_default_content_type(); + assert!(settings.include_header(&CONTENT_LENGTH)); + assert!(!settings.include_header(&CONTENT_TYPE)); + + let settings = HeaderSerializationSettings::default() + .omit_default_content_type() + .omit_default_content_length(); + assert!(!settings.include_header(&CONTENT_LENGTH)); + assert!(!settings.include_header(&CONTENT_TYPE)); + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f701aa5354..f2415f8315 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.66.1" +channel = "1.69.0" diff --git a/settings.gradle.kts b/settings.gradle.kts index 88f55a4e96..6c920d7217 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,14 +10,15 @@ include(":codegen-client") include(":codegen-client-test") include(":codegen-server") include(":codegen-server:python") +include(":codegen-server:typescript") include(":codegen-server-test") include(":codegen-server-test:python") +include(":codegen-server-test:typescript") include(":rust-runtime") include(":aws:rust-runtime") include(":aws:sdk") include(":aws:sdk-adhoc-test") include(":aws:sdk-codegen") -include(":aws:sra-test") pluginManagement { val smithyGradlePluginVersion: String by settings diff --git a/tools/.cargo/config.toml b/tools/.cargo/config.toml index 2bf2a2e3e4..6636645498 100644 --- a/tools/.cargo/config.toml +++ b/tools/.cargo/config.toml @@ -2,3 +2,7 @@ # Tools shouldn't share `target` with the rest of the project to # avoid thrash from differing compiler flags target-dir = "target" + +# TODO(Rust 1.70): The sparse registry config can be removed when upgrading to Rust 1.70 +[registries.crates-io] +protocol = "sparse" diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index 0e64f746a9..f1e74fc79f 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -6,8 +6,8 @@ # This is the base Docker build image used by CI ARG base_image=public.ecr.aws/amazonlinux/amazonlinux:2 -ARG rust_stable_version=1.66.1 -ARG rust_nightly_version=nightly-2022-11-16 +ARG rust_stable_version=1.69.0 +ARG rust_nightly_version=nightly-2023-05-31 FROM ${base_image} AS bare_base_image RUN yum -y updateinfo @@ -80,53 +80,46 @@ RUN set -eux; \ cd smithy-rs; \ git checkout ${smithy_rs_commit_hash}; \ fi; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/publisher; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/changelogger; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/crate-hasher; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/difftags; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/sdk-lints; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/sdk-versioner; \ + cargo install --locked --path tools/ci-build/publisher; \ + cargo install --locked --path tools/ci-build/changelogger; \ + cargo install --locked --path tools/ci-build/crate-hasher; \ + cargo install --locked --path tools/ci-build/difftags; \ + cargo install --locked --path tools/ci-build/sdk-lints; \ + cargo install --locked --path tools/ci-build/sdk-versioner; \ chmod g+rw -R /opt/cargo/registry FROM install_rust AS cargo_deny ARG cargo_deny_version=0.13.5 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-deny --locked --version ${cargo_deny_version} +RUN cargo install cargo-deny --locked --version ${cargo_deny_version} FROM install_rust AS cargo_udeps ARG cargo_udeps_version=0.1.35 ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-udeps --locked --version ${cargo_udeps_version} +RUN cargo +${rust_nightly_version} install cargo-udeps --locked --version ${cargo_udeps_version} FROM install_rust AS cargo_hack ARG cargo_hack_version=0.5.23 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-hack --locked --version ${cargo_hack_version} +RUN cargo install cargo-hack --locked --version ${cargo_hack_version} FROM install_rust AS cargo_minimal_versions ARG cargo_minimal_versions_version=0.1.8 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-minimal-versions --locked --version ${cargo_minimal_versions_version} +RUN cargo install cargo-minimal-versions --locked --version ${cargo_minimal_versions_version} FROM install_rust AS cargo_check_external_types -ARG cargo_check_external_types_version=0.1.6 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-check-external-types --locked --version ${cargo_check_external_types_version} +ARG cargo_check_external_types_version=0.1.7 +RUN cargo install cargo-check-external-types --locked --version ${cargo_check_external_types_version} FROM install_rust AS maturin ARG maturin_version=0.14.1 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install maturin --locked --version ${maturin_version} +RUN cargo install maturin --locked --version ${maturin_version} FROM install_rust AS wasm_pack ARG wasm_pack_version=0.11.0 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install wasm-pack --locked --version ${wasm_pack_version} +RUN cargo install wasm-pack --locked --version ${wasm_pack_version} FROM install_rust AS wasmtime ARG wasmtime_precompiled_url=https://github.com/bytecodealliance/wasmtime/releases/download/v7.0.0/wasmtime-v7.0.0-x86_64-linux.tar.xz ARG wasmtime_precompiled_sha256=b8a1c97f9107c885ea73a5c38677d0d340a7c26879d366e8a5f3dce84cffec99 -ARG rust_nightly_version RUN set -eux; \ curl "${wasmtime_precompiled_url}" -L -o wasmtime.xz; \ echo "${wasmtime_precompiled_sha256} wasmtime.xz" | sha256sum --check; \ @@ -135,13 +128,23 @@ RUN set -eux; \ FROM install_rust AS cargo_wasi ARG cargo_wasi_version=0.1.27 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-wasi --locked --version ${cargo_wasi_version} +RUN cargo install cargo-wasi --locked --version ${cargo_wasi_version} FROM install_rust AS cargo_semver_checks -ARG cargo_semver_checks_version=0.19.0 +ARG cargo_semver_checks_version=0.20.0 ARG rust_nightly_version RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-semver-checks --locked --version ${cargo_semver_checks_version} + +FROM install_rust AS cargo_mdbook +ARG cargo_mdbook_version=0.4.30 +ARG rust_nightly_version +RUN cargo +${rust_nightly_version} -Z sparse-registry install mdbook --locked --version ${cargo_mdbook_version} + +FROM install_rust AS cargo_mdbook_mermaid +ARG cargo_mdbook_mermaid_version=0.12.6 +ARG rust_nightly_version +RUN cargo +${rust_nightly_version} -Z sparse-registry install mdbook-mermaid --locked --version ${cargo_mdbook_mermaid_version} + # # Final image # @@ -179,8 +182,11 @@ COPY --chown=build:build --from=wasmtime /opt/wasmtime /opt/cargo/bin/wasmtime COPY --chown=build:build --from=cargo_wasi /opt/cargo/bin/cargo-wasi /opt/cargo/bin/cargo-wasi COPY --chown=build:build --from=install_rust /opt/rustup /opt/rustup COPY --chown=build:build --from=cargo_semver_checks /opt/cargo/bin/cargo-semver-checks /opt/cargo/bin/cargo-semver-checks +COPY --chown=build:build --from=cargo_mdbook /opt/cargo/bin/mdbook /opt/cargo/bin/mdbook +COPY --chown=build:build --from=cargo_mdbook_mermaid /opt/cargo/bin/mdbook-mermaid /opt/cargo/bin/mdbook-mermaid COPY --chown=build:build --from=musl_toolchain /usr/local/musl/ /usr/local/musl/ ENV PATH=$PATH:/usr/local/musl/bin/ +# TODO(Rust 1.70): The sparse registry config (`CARGO_REGISTRIES_CRATES_IO_PROTOCOL`) can be removed when upgrading to Rust 1.70 ENV PATH=/opt/cargo/bin:$PATH \ CARGO_HOME=/opt/cargo \ RUSTUP_HOME=/opt/rustup \ @@ -188,6 +194,7 @@ ENV PATH=/opt/cargo/bin:$PATH \ GRADLE_USER_HOME=/home/build/.gradle \ RUST_STABLE_VERSION=${rust_stable_version} \ RUST_NIGHTLY_VERSION=${rust_nightly_version} \ + CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse \ CARGO_INCREMENTAL=0 \ RUSTDOCFLAGS="-D warnings" \ RUSTFLAGS="-D warnings" \ @@ -200,4 +207,7 @@ ENV SMITHY_RS_DOCKER_BUILD_IMAGE=1 RUN pip3 install --no-cache-dir mypy==0.991 WORKDIR /home/build COPY sanity-test /home/build/sanity-test +# RUSTUP_TOOLCHAIN takes precedence over everything except `+` args. This will allow us to ignore the toolchain +# file during CI, avoiding issues during Rust version upgrades. +ENV RUSTUP_TOOLCHAIN=${rust_stable_version} RUN /home/build/sanity-test diff --git a/tools/ci-build/changelogger/Cargo.lock b/tools/ci-build/changelogger/Cargo.lock index 4ee3df2db4..682f4e1217 100644 --- a/tools/ci-build/changelogger/Cargo.lock +++ b/tools/ci-build/changelogger/Cargo.lock @@ -2,30 +2,45 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -45,11 +60,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.13.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -57,23 +87,29 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -100,12 +136,12 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.22" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -117,15 +153,15 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -149,19 +185,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "ctor" -version = "0.1.23" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" -dependencies = [ - "quote", - "syn", -] +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "diff" @@ -171,22 +197,40 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] [[package]] -name = "fastrand" -version = "1.8.0" +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" dependencies = [ - "instant", + "cc", + "libc", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fnv" version = "1.0.7" @@ -210,45 +254,45 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -256,11 +300,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.14" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -283,9 +333,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -298,9 +348,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -332,9 +382,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -369,9 +419,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -379,40 +429,31 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" -version = "2.5.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -425,18 +466,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -446,27 +490,35 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", "windows-sys", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -492,9 +544,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -508,19 +560,28 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.15.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -531,13 +592,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -548,11 +609,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -570,30 +630,21 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" - -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -603,19 +654,17 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -628,7 +677,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -645,62 +694,65 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] -name = "regex-syntax" -version = "0.6.27" +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -733,29 +785,47 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "lazy_static", "windows-sys", ] [[package]] name = "security-framework" -version = "2.7.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -764,9 +834,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -774,35 +844,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -823,9 +893,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -848,9 +918,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -864,9 +934,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -875,43 +956,50 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "time" -version = "0.3.15" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ "libc", "num_threads", + "serde", + "time-core", ] +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + [[package]] name = "tinyvec" version = "1.6.0" @@ -923,31 +1011,31 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "pin-project-lite", "socket2", - "winapi", + "windows-sys", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -955,9 +1043,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -969,9 +1057,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "indexmap", "serde", @@ -985,9 +1073,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", @@ -997,41 +1085,41 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -1044,9 +1132,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1067,11 +1155,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1083,9 +1170,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1093,24 +1180,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1120,9 +1207,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1130,28 +1217,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1190,46 +1277,69 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" diff --git a/tools/ci-build/changelogger/smithy-rs-maintainers.txt b/tools/ci-build/changelogger/smithy-rs-maintainers.txt index 4d980016b9..f5732607b1 100644 --- a/tools/ci-build/changelogger/smithy-rs-maintainers.txt +++ b/tools/ci-build/changelogger/smithy-rs-maintainers.txt @@ -1,15 +1,7 @@ 82marbag aws-sdk-rust-ci -crisidev david-perez -drganjoo -hlbarber jdisanti -jjant -LukeMathWalker -pose rcoh -unexge velfi -weihanglo ysaito1001 diff --git a/tools/ci-build/changelogger/src/render.rs b/tools/ci-build/changelogger/src/render.rs index 4dd8e22c1e..445d2e8d70 100644 --- a/tools/ci-build/changelogger/src/render.rs +++ b/tools/ci-build/changelogger/src/render.rs @@ -175,13 +175,13 @@ fn to_md_link(reference: &Reference) -> String { fn render_entry(entry: &HandAuthoredEntry, mut out: &mut String) { let mut meta = String::new(); if entry.meta.bug { - meta.push('🐛'); + meta.push_str(":bug:"); } if entry.meta.breaking { - meta.push('⚠'); + meta.push_str(":warning:"); } if entry.meta.tada { - meta.push('🎉'); + meta.push_str(":tada:"); } if !meta.is_empty() { meta.push(' '); @@ -562,11 +562,11 @@ message = "Some API change" v0.3.0 (January 4th, 2022) ========================== **Breaking Changes:** -- ⚠ (all, [smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the code generator +- :warning: (all, [smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the code generator **New this release:** -- 🎉 (all, [smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator -- 🎉 (all, [smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator +- :tada: (all, [smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator +- :tada: (all, [smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator **Update guide:** blah blah @@ -586,10 +586,10 @@ Thank you for your contributions! ❤ v0.1.0 (January 4th, 2022) ========================== **Breaking Changes:** -- ⚠ ([smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the AWS SDK +- :warning: ([smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the AWS SDK **New this release:** -- 🎉 ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator +- :tada: ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator **Service Features:** - `aws-sdk-ec2` (0.12.0): Some API change diff --git a/tools/ci-build/changelogger/tests/e2e_test.rs b/tools/ci-build/changelogger/tests/e2e_test.rs index 745e73ef47..302607704a 100644 --- a/tools/ci-build/changelogger/tests/e2e_test.rs +++ b/tools/ci-build/changelogger/tests/e2e_test.rs @@ -393,7 +393,7 @@ fn render_aws_sdk() { January 1st, 1970 ================= **New this release:** -- 🐛 ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567), @test-dev) Some other change +- :bug: ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567), @test-dev) Some other change **Service Features:** - `aws-sdk-ec2` (0.12.0): Some API change @@ -414,7 +414,7 @@ Old entry contents r#"{ "tagName": "release-1970-01-01", "name": "January 1st, 1970", - "body": "**New this release:**\n- 🐛 ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567), @test-dev) Some other change\n\n**Service Features:**\n- `aws-sdk-ec2` (0.12.0): Some API change\n\n**Contributors**\nThank you for your contributions! ❤\n- @test-dev ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567))\n\n", + "body": "**New this release:**\n- :bug: ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567), @test-dev) Some other change\n\n**Service Features:**\n- `aws-sdk-ec2` (0.12.0): Some API change\n\n**Contributors**\nThank you for your contributions! ❤\n- @test-dev ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567))\n\n", "prerelease": true }"#, release_manifest @@ -467,7 +467,7 @@ author = "rcoh" message = "Fourth change - client" references = ["smithy-rs#4"] meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } -author = "LukeMathWalker" +author = "rcoh" "#; let tmp_dir = TempDir::new().unwrap(); let source_path = tmp_dir.path().join("source.toml"); @@ -511,7 +511,7 @@ author = "LukeMathWalker" January 1st, 1970 ================= **Breaking Changes:** -- ⚠ (all, [smithy-rs#3](https://github.com/awslabs/smithy-rs/issues/3)) Third change - empty +- :warning: (all, [smithy-rs#3](https://github.com/awslabs/smithy-rs/issues/3)) Third change - empty **New this release:** - (server, [smithy-rs#1](https://github.com/awslabs/smithy-rs/issues/1), @server-dev) First change - server diff --git a/tools/ci-build/crate-hasher/Cargo.lock b/tools/ci-build/crate-hasher/Cargo.lock index c6f1912dd2..bc931d5984 100644 --- a/tools/ci-build/crate-hasher/Cargo.lock +++ b/tools/ci-build/crate-hasher/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -10,18 +19,29 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "async-trait" +version = "0.1.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] [[package]] name = "atty" @@ -40,30 +60,64 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bstr" -version = "0.2.17" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", + "serde", ] +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" @@ -77,7 +131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -97,7 +151,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -111,9 +165,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -129,7 +183,7 @@ dependencies = [ "pretty_assertions", "sha256", "tar", - "tempdir", + "tempfile", ] [[package]] @@ -143,21 +197,21 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] -name = "ctor" -version = "0.1.23" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "quote", - "syn", + "generic-array", + "typenum", ] [[package]] @@ -168,30 +222,58 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.9.0" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "generic-array", + "block-buffer", + "crypto-common", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", ] +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "filetime" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "windows-sys", ] [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -203,27 +285,27 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "globset" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" dependencies = [ "aho-corasick", "bstr", @@ -240,9 +322,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -279,9 +361,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -295,18 +377,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -316,49 +401,47 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] -name = "once_cell" -version = "1.15.0" +name = "object" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "once_cell" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] -name = "output_vt100" -version = "0.1.3" +name = "pin-project-lite" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -371,7 +454,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -388,73 +471,57 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_core" -version = "0.3.1" +name = "redox_syscall" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "rand_core 0.4.2", + "bitflags 1.3.2", ] [[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rdrand" -version = "0.4.0" +name = "redox_syscall" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "rand_core 0.3.1", + "bitflags 1.3.2", ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "regex" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "bitflags", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "regex" -version = "1.6.0" +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -463,17 +530,27 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "winapi", + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", ] [[package]] @@ -485,27 +562,34 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "serde" +version = "1.0.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" + [[package]] name = "sha2" -version = "0.9.9" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ - "block-buffer", "cfg-if", "cpufeatures", "digest", - "opaque-debug", ] [[package]] name = "sha256" -version = "1.1.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141" +checksum = "386f700b0c798d92ac20a53342c240ff9d58030c3b845fbaeb92eead3a774792" dependencies = [ + "async-trait", + "bytes", "hex", "sha2", + "tokio", ] [[package]] @@ -516,9 +600,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -527,9 +622,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "ec96d2ffad078296368d46ff1cb309be1c23c513b4ab0e22a45de0185275ac96" dependencies = [ "filetime", "libc", @@ -537,50 +632,66 @@ dependencies = [ ] [[package]] -name = "tempdir" -version = "0.3.7" +name = "tempfile" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ - "rand", - "remove_dir_all", + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "pin-project-lite", +] + [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "version_check" @@ -590,12 +701,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -632,46 +742,69 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "xattr" diff --git a/tools/ci-build/crate-hasher/Cargo.toml b/tools/ci-build/crate-hasher/Cargo.toml index 3f9376443a..2643b9804d 100644 --- a/tools/ci-build/crate-hasher/Cargo.toml +++ b/tools/ci-build/crate-hasher/Cargo.toml @@ -24,4 +24,4 @@ sha256 = "1.1" flate2 = "1.0" pretty_assertions = "1.3" tar = "0.4" -tempdir = "0.3" +tempfile = "3.5.0" diff --git a/tools/ci-build/crate-hasher/tests/test.rs b/tools/ci-build/crate-hasher/tests/test.rs index cc46d03dab..98bd360bb4 100644 --- a/tools/ci-build/crate-hasher/tests/test.rs +++ b/tools/ci-build/crate-hasher/tests/test.rs @@ -8,7 +8,7 @@ use flate2::read::GzDecoder; use std::fs::File; use std::process::Command; use tar::Archive; -use tempdir::TempDir; +use tempfile::TempDir; use crate_hasher::file_list::FileList; @@ -25,32 +25,32 @@ fn assert_correct_aws_smithy_async_hash(file_list: &FileList) { #[test] fn test_against_aws_smithy_async() -> Result<()> { - let dir = TempDir::new("test_against_aws_smithy_async")?; + let dir = TempDir::new()?.path().join("test_against_aws_smithy_async"); let tar = GzDecoder::new(File::open("tests/aws-smithy-async-2022-04-08.tar.gz")?); let mut archive = Archive::new(tar); archive.unpack(&dir)?; - let file_list = FileList::discover(&dir.as_ref().join("aws-smithy-async"))?; + let file_list = FileList::discover(&dir.as_path().join("aws-smithy-async"))?; assert_correct_aws_smithy_async_hash(&file_list); Ok(()) } #[test] fn test_against_aws_smithy_async_with_ignored_files() -> Result<()> { - let dir = TempDir::new("test_against_aws_smithy_async")?; + let dir = TempDir::new()?.path().join("test_against_aws_smithy_async"); let tar = GzDecoder::new(File::open("tests/aws-smithy-async-2022-04-08.tar.gz")?); let mut archive = Archive::new(tar); archive.unpack(&dir)?; - std::fs::create_dir(dir.as_ref().join("target"))?; + std::fs::create_dir(&dir.as_path().join("target"))?; std::fs::write( - dir.as_ref().join("target/something"), + &dir.as_path().join("target/something"), b"some data that should be excluded", )?; - let file_list = FileList::discover(&dir.as_ref().join("aws-smithy-async"))?; + let file_list = FileList::discover(&dir.as_path().join("aws-smithy-async"))?; assert_correct_aws_smithy_async_hash(&file_list); Ok(()) @@ -58,7 +58,7 @@ fn test_against_aws_smithy_async_with_ignored_files() -> Result<()> { #[test] fn test_against_aws_smithy_async_with_git_repo() -> Result<()> { - let dir = TempDir::new("test_against_aws_smithy_async")?; + let dir = TempDir::new()?.path().join("test_against_aws_smithy_async"); let tar = GzDecoder::new(File::open("tests/aws-smithy-async-2022-04-08.tar.gz")?); let mut archive = Archive::new(tar); @@ -68,10 +68,10 @@ fn test_against_aws_smithy_async_with_git_repo() -> Result<()> { Command::new("git") .arg("init") .arg(".") - .current_dir(dir.as_ref().join("aws-smithy-async")) + .current_dir(&dir.as_path().join("aws-smithy-async")) .output()?; - let file_list = FileList::discover(&dir.as_ref().join("aws-smithy-async"))?; + let file_list = FileList::discover(&dir.as_path().join("aws-smithy-async"))?; assert_correct_aws_smithy_async_hash(&file_list); Ok(()) diff --git a/tools/ci-build/difftags/Cargo.lock b/tools/ci-build/difftags/Cargo.lock index 849338e259..1bbe447c25 100644 --- a/tools/ci-build/difftags/Cargo.lock +++ b/tools/ci-build/difftags/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -36,9 +36,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags", @@ -53,9 +53,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", @@ -90,9 +90,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -114,9 +114,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -130,9 +130,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "memchr" @@ -142,15 +142,15 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "once_cell" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "proc-macro-error" @@ -178,27 +178,39 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -207,9 +219,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "strsim" @@ -219,9 +231,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -245,9 +257,9 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unidiff" diff --git a/tools/ci-build/publisher/Cargo.lock b/tools/ci-build/publisher/Cargo.lock index 450e0b81b5..4ac8cf16e6 100644 --- a/tools/ci-build/publisher/Cargo.lock +++ b/tools/ci-build/publisher/Cargo.lock @@ -2,20 +2,41 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-recursion" @@ -25,18 +46,18 @@ checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -56,11 +77,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -69,28 +105,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "block-buffer" -version = "0.9.0" +name = "bitflags" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" @@ -123,11 +156,11 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ - "num-integer", + "android-tzdata", "num-traits", "serde", ] @@ -139,7 +172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -159,7 +192,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -198,15 +231,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -238,16 +271,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "dialoguer" version = "0.8.0" @@ -268,20 +291,11 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.9.0" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.3", + "block-buffer", "crypto-common", ] @@ -301,14 +315,32 @@ dependencies = [ ] [[package]] -name = "fastrand" -version = "1.8.0" +name = "errno" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ - "instant", + "errno-dragonfly", + "libc", + "windows-sys", ] +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fnv" version = "1.0.7" @@ -332,9 +364,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -347,9 +379,9 @@ checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -362,9 +394,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -372,15 +404,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -389,38 +421,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -436,19 +468,25 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.15" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -465,9 +503,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.6" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" dependencies = [ "log", "pest", @@ -500,12 +538,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -515,9 +550,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -549,9 +584,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -586,9 +621,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -596,40 +631,31 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -642,15 +668,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -658,12 +690,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "matchers" @@ -671,7 +700,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -682,20 +711,28 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -726,54 +763,47 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.2", "libc", ] [[package]] -name = "once_cell" -version = "1.17.0" +name = "object" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "once_cell" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -784,13 +814,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -801,11 +831,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -814,18 +843,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "overload" @@ -845,28 +865,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.5.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" +checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" dependencies = [ "thiserror", "ucd-trie", @@ -874,9 +894,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" +checksum = "5f94bca7e7a599d89dea5dfa309e217e7906c3c007fb9c3299c40b10d6a315d3" dependencies = [ "pest", "pest_generator", @@ -884,33 +904,33 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" +checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "pest_meta" -version = "2.5.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" +checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -920,19 +940,17 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -945,7 +963,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -962,9 +980,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -1000,31 +1018,32 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", ] [[package]] @@ -1033,29 +1052,37 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -1088,34 +1115,53 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1124,9 +1170,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -1134,35 +1180,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.92" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -1183,36 +1229,26 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.9" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ - "block-buffer 0.9.0", "cfg-if", "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", + "digest", ] [[package]] name = "sha256" -version = "1.1.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141" +checksum = "386f700b0c798d92ac20a53342c240ff9d58030c3b845fbaeb92eead3a774792" dependencies = [ + "async-trait", + "bytes", "hex", - "sha2 0.9.9", + "sha2", + "tokio", ] [[package]] @@ -1226,27 +1262,27 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "smithy-rs-tool-common" @@ -1267,9 +1303,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1283,9 +1319,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -1294,16 +1341,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys", ] [[package]] @@ -1333,30 +1379,31 @@ checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -1377,14 +1424,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -1392,25 +1439,25 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -1418,9 +1465,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -1460,20 +1507,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -1492,9 +1539,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -1522,21 +1569,21 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -1555,9 +1602,9 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1584,11 +1631,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1600,9 +1646,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1610,24 +1656,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1637,9 +1683,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1647,28 +1693,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1707,33 +1753,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -1746,45 +1777,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" @@ -1803,6 +1834,6 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/tools/ci-build/publisher/Cargo.toml b/tools/ci-build/publisher/Cargo.toml index f076cedacf..772b80ae0d 100644 --- a/tools/ci-build/publisher/Cargo.toml +++ b/tools/ci-build/publisher/Cargo.toml @@ -30,12 +30,12 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" sha256 = "1" smithy-rs-tool-common = { version = "0.1", path = "../smithy-rs-tool-common", features = ["async-shell"] } +tempfile = "3.3.0" thiserror = "1.0" tokio = { version = "1.20.1", features = ["full"] } toml = { version = "0.5.8", features = ["preserve_order"] } tracing = "0.1.29" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } -tempfile = "3.3.0" [dev-dependencies] pretty_assertions = "1.3" diff --git a/tools/ci-build/publisher/src/package.rs b/tools/ci-build/publisher/src/package.rs index 2b0bc90764..c0cd981b0b 100644 --- a/tools/ci-build/publisher/src/package.rs +++ b/tools/ci-build/publisher/src/package.rs @@ -145,8 +145,7 @@ pub async fn discover_and_validate_package_batches( fs: Fs, path: impl AsRef, ) -> Result<(Vec, PackageStats)> { - let manifest_paths = discover_package_manifests(path.as_ref().into()).await?; - let packages = read_packages(fs, manifest_paths) + let packages = discover_packages(fs, path.as_ref().into()) .await? .into_iter() .filter(|package| package.publish == Publish::Allowed) @@ -176,7 +175,7 @@ pub enum Error { /// Discovers all Cargo.toml files under the given path recursively #[async_recursion::async_recursion] -pub async fn discover_package_manifests(path: PathBuf) -> Result> { +pub async fn discover_manifests(path: PathBuf) -> Result> { let mut manifests = Vec::new(); let mut read_dir = fs::read_dir(&path).await?; while let Some(entry) = read_dir.next_entry().await? { @@ -185,14 +184,19 @@ pub async fn discover_package_manifests(path: PathBuf) -> Result> { let manifest_path = package_path.join("Cargo.toml"); if manifest_path.exists() { manifests.push(manifest_path); - } else { - manifests.extend(discover_package_manifests(package_path).await?.into_iter()); } + manifests.extend(discover_manifests(package_path).await?.into_iter()); } } Ok(manifests) } +/// Discovers and parses all Cargo.toml files that are packages (as opposed to being exclusively workspaces) +pub async fn discover_packages(fs: Fs, path: PathBuf) -> Result> { + let manifest_paths = discover_manifests(path).await?; + read_packages(fs, manifest_paths).await +} + /// Parses a semver version number and adds additional error context when parsing fails. pub fn parse_version(manifest_path: &Path, version: &str) -> Result { Version::parse(version) @@ -219,26 +223,33 @@ fn read_dependencies(path: &Path, dependencies: &DepsSet) -> Result Result { +/// Returns `Ok(None)` when the Cargo.toml is a workspace rather than a package +fn read_package(path: &Path, manifest_bytes: &[u8]) -> Result> { let manifest = Manifest::from_slice(manifest_bytes) .with_context(|| format!("failed to load package manifest for {:?}", path))?; - let package = manifest - .package - .ok_or_else(|| Error::InvalidManifest(path.into())) - .context("crate manifest doesn't have a `[package]` section")?; - let name = package.name; - let version = parse_version(path, &package.version)?; - let handle = PackageHandle { name, version }; - let publish = match package.publish { - cargo_toml::Publish::Flag(true) => Publish::Allowed, - _ => Publish::NotAllowed, - }; - - let mut local_dependencies = BTreeSet::new(); - local_dependencies.extend(read_dependencies(path, &manifest.dependencies)?.into_iter()); - local_dependencies.extend(read_dependencies(path, &manifest.dev_dependencies)?.into_iter()); - local_dependencies.extend(read_dependencies(path, &manifest.build_dependencies)?.into_iter()); - Ok(Package::new(handle, path, local_dependencies, publish)) + if let Some(package) = manifest.package { + let name = package.name; + let version = parse_version(path, &package.version)?; + let handle = PackageHandle { name, version }; + let publish = match package.publish { + cargo_toml::Publish::Flag(true) => Publish::Allowed, + _ => Publish::NotAllowed, + }; + + let mut local_dependencies = BTreeSet::new(); + local_dependencies.extend(read_dependencies(path, &manifest.dependencies)?.into_iter()); + local_dependencies.extend(read_dependencies(path, &manifest.dev_dependencies)?.into_iter()); + local_dependencies + .extend(read_dependencies(path, &manifest.build_dependencies)?.into_iter()); + Ok(Some(Package::new( + handle, + path, + local_dependencies, + publish, + ))) + } else { + Ok(None) + } } /// Validates that all of the publishable crates use consistent version numbers @@ -275,7 +286,9 @@ pub async fn read_packages(fs: Fs, manifest_paths: Vec) -> Result = fs.read_file(path).await?; - result.push(read_package(path, &contents)?); + if let Some(package) = read_package(path, &contents)? { + result.push(package); + } } Ok(result) } @@ -350,7 +363,9 @@ mod tests { "#; let path: PathBuf = "test/Cargo.toml".into(); - let package = read_package(&path, manifest).expect("parse success"); + let package = read_package(&path, manifest) + .expect("parse success") + .expect("is a package"); assert_eq!("test", package.handle.name); assert_eq!(version("1.2.0-preview"), package.handle.version); @@ -521,6 +536,7 @@ mod tests { let server_packages = vec![ package("aws-smithy-http-server", &[]), package("aws-smithy-http-server-python", &[]), + package("aws-smithy-http-server-typescript", &[]), ]; for pkg in server_packages { assert_eq!( diff --git a/tools/ci-build/publisher/src/subcommand/claim_crate_names.rs b/tools/ci-build/publisher/src/subcommand/claim_crate_names.rs index 72a3285ddb..ae7a41b304 100644 --- a/tools/ci-build/publisher/src/subcommand/claim_crate_names.rs +++ b/tools/ci-build/publisher/src/subcommand/claim_crate_names.rs @@ -3,12 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ use crate::fs::Fs; -use crate::package::{discover_package_manifests, Error, PackageHandle}; +use crate::package::{discover_packages, PackageHandle, Publish}; use crate::publish::{has_been_published_on_crates_io, publish}; use crate::subcommand::publish::correct_owner; use crate::{cargo, SDK_REPO_NAME}; -use anyhow::Context; -use cargo_toml::Manifest; use clap::Parser; use dialoguer::Confirm; use semver::Version; @@ -79,20 +77,11 @@ async fn discover_publishable_crate_names(repository_root: &Path) -> anyhow::Res fs: Fs, path: PathBuf, ) -> anyhow::Result> { - let manifest_paths = discover_package_manifests(path).await?; + let packages = discover_packages(fs, path).await?; let mut publishable_package_names = HashSet::new(); - for manifest_path in manifest_paths { - let contents: Vec = fs.read_file(&manifest_path).await?; - let manifest = Manifest::from_slice(&contents).with_context(|| { - format!("failed to load package manifest for {:?}", manifest_path) - })?; - let package = manifest - .package - .ok_or(Error::InvalidManifest(manifest_path)) - .context("crate manifest doesn't have a `[package]` section")?; - let name = package.name; - if let cargo_toml::Publish::Flag(true) = package.publish { - publishable_package_names.insert(name); + for package in packages { + if let Publish::Allowed = package.publish { + publishable_package_names.insert(package.handle.name); } } Ok(publishable_package_names) diff --git a/tools/ci-build/publisher/src/subcommand/fix_manifests.rs b/tools/ci-build/publisher/src/subcommand/fix_manifests.rs index c6c15afceb..2d34c1aba7 100644 --- a/tools/ci-build/publisher/src/subcommand/fix_manifests.rs +++ b/tools/ci-build/publisher/src/subcommand/fix_manifests.rs @@ -10,7 +10,7 @@ //! version numbers in addition to the dependency path. use crate::fs::Fs; -use crate::package::{discover_package_manifests, parse_version}; +use crate::package::{discover_manifests, parse_version}; use crate::SDK_REPO_NAME; use anyhow::{bail, Context, Result}; use clap::Parser; @@ -20,7 +20,8 @@ use std::collections::BTreeMap; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use toml::value::Table; -use tracing::info; +use toml::Value; +use tracing::{debug, info}; mod validate; @@ -55,7 +56,7 @@ pub async fn subcommand_fix_manifests( true => Mode::Check, false => Mode::Execute, }; - let manifest_paths = discover_package_manifests(location.into()).await?; + let manifest_paths = discover_manifests(location.into()).await?; let mut manifests = read_manifests(Fs::Real, manifest_paths).await?; let versions = package_versions(&manifests)?; @@ -71,6 +72,63 @@ struct Manifest { metadata: toml::Value, } +impl Manifest { + /// Returns the `publish` setting for a given crate + fn publish(&self) -> Result { + let value = self.metadata.get("package").and_then(|v| v.get("publish")); + match value { + None => Ok(true), + Some(value) => value + .as_bool() + .ok_or(anyhow::Error::msg("unexpected publish setting")), + } + } +} + +struct Versions(BTreeMap); +#[derive(Copy, Clone)] +enum FilterType { + AllCrates, + PublishedOnly, +} +struct VersionView<'a>(&'a Versions, FilterType); +impl VersionView<'_> { + fn get(&self, crate_name: &str) -> Option<&Version> { + let version = match (self.1, self.0 .0.get(crate_name)) { + (FilterType::AllCrates, version) => version, + (FilterType::PublishedOnly, v @ Some(VersionWithMetadata { publish: true, .. })) => v, + _ => None, + }; + version.map(|v| &v.version) + } + + fn all_crates(&self) -> Self { + VersionView(self.0, FilterType::AllCrates) + } +} + +impl Versions { + fn published(&self) -> VersionView { + VersionView(self, FilterType::PublishedOnly) + } + + fn published_crates(&self) -> impl Iterator + '_ { + self.0 + .iter() + .filter(|(_, v)| v.publish) + .map(|(k, v)| (k.as_str(), &v.version)) + } + + fn get(&self, crate_name: &str) -> Option<&Version> { + self.0.get(crate_name).map(|v| &v.version) + } +} + +struct VersionWithMetadata { + version: Version, + publish: bool, +} + async fn read_manifests(fs: Fs, manifest_paths: Vec) -> Result> { let mut result = Vec::new(); for path in manifest_paths { @@ -83,7 +141,7 @@ async fn read_manifests(fs: Fs, manifest_paths: Vec) -> Result Result> { +fn package_versions(manifests: &[Manifest]) -> Result { let mut versions = BTreeMap::new(); for manifest in manifests { // ignore workspace manifests @@ -91,6 +149,7 @@ fn package_versions(manifests: &[Manifest]) -> Result> Some(package) => package, None => continue, }; + let publish = manifest.publish()?; let name = package .get("name") .and_then(|name| name.as_str()) @@ -104,16 +163,12 @@ fn package_versions(manifests: &[Manifest]) -> Result> anyhow::Error::msg(format!("{:?} is missing a package version", manifest.path)) })?; let version = parse_version(&manifest.path, version)?; - versions.insert(name.into(), version); + versions.insert(name.into(), VersionWithMetadata { version, publish }); } - Ok(versions) + Ok(Versions(versions)) } -fn fix_dep_set( - versions: &BTreeMap, - key: &str, - metadata: &mut toml::Value, -) -> Result { +fn fix_dep_set(versions: &VersionView, key: &str, metadata: &mut Value) -> Result { let mut changed = 0; if let Some(dependencies) = metadata.as_table_mut().unwrap().get_mut(key) { if let Some(dependencies) = dependencies.as_table_mut() { @@ -133,11 +188,7 @@ fn fix_dep_set( Ok(changed) } -fn update_dep( - table: &mut Table, - dep_name: &str, - versions: &BTreeMap, -) -> Result { +fn update_dep(table: &mut Table, dep_name: &str, versions: &VersionView) -> Result { if !table.contains_key("path") { return Ok(0); } @@ -159,9 +210,10 @@ fn update_dep( } } -fn fix_dep_sets(versions: &BTreeMap, metadata: &mut toml::Value) -> Result { +fn fix_dep_sets(versions: &VersionView, metadata: &mut toml::Value) -> Result { let mut changed = fix_dep_set(versions, "dependencies", metadata)?; - changed += fix_dep_set(versions, "dev-dependencies", metadata)?; + // allow dev dependencies to be unpublished + changed += fix_dep_set(&versions.all_crates(), "dev-dependencies", metadata)?; changed += fix_dep_set(versions, "build-dependencies", metadata)?; Ok(changed) } @@ -192,45 +244,53 @@ fn conditionally_disallow_publish( // is not being run from CI, and disallow publish in that case. Also disallow // publishing of examples. if !is_github_actions || is_example { - if let Some(package) = metadata.as_table_mut().unwrap().get_mut("package") { - info!( - "Detected {}. Disallowing publish for {:?}.", - if is_example { "example" } else { "local build" }, - manifest_path, - ); - package - .as_table_mut() - .unwrap() - .insert("publish".into(), toml::Value::Boolean(false)); - return Ok(true); + if let Some(value) = set_publish_false(manifest_path, metadata, is_example) { + return Ok(value); } } Ok(false) } +fn set_publish_false(manifest_path: &Path, metadata: &mut Value, is_example: bool) -> Option { + if let Some(package) = metadata.as_table_mut().unwrap().get_mut("package") { + info!( + "Detected {}. Disallowing publish for {:?}.", + if is_example { "example" } else { "local build" }, + manifest_path, + ); + package + .as_table_mut() + .unwrap() + .insert("publish".into(), toml::Value::Boolean(false)); + return Some(true); + } + None +} + async fn fix_manifests( fs: Fs, - versions: &BTreeMap, + versions: &Versions, manifests: &mut Vec, mode: Mode, ) -> Result<()> { for manifest in manifests { let package_changed = conditionally_disallow_publish(&manifest.path, &mut manifest.metadata)?; - let dependencies_changed = fix_dep_sets(versions, &mut manifest.metadata)?; - if package_changed || dependencies_changed > 0 { + let num_deps_changed = fix_manifest(versions, manifest)?; + if package_changed || num_deps_changed > 0 { let contents = "# Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.\n" .to_string() + &toml::to_string(&manifest.metadata).with_context(|| { format!("failed to serialize to toml for {:?}", manifest.path) })?; + match mode { Mode::Execute => { fs.write_file(&manifest.path, contents.as_bytes()).await?; info!( "Changed {} dependencies in {:?}.", - dependencies_changed, manifest.path + num_deps_changed, manifest.path ); } Mode::Check => { @@ -245,10 +305,73 @@ async fn fix_manifests( Ok(()) } +fn fix_manifest(versions: &Versions, manifest: &mut Manifest) -> Result { + let mut view = versions.published(); + if !manifest.publish()? { + debug!(package = ?&manifest.path, "package has publishing disabled, allowing unpublished crates to be used"); + view = view.all_crates(); + } + fix_dep_sets(&view, &mut manifest.metadata) +} + #[cfg(test)] mod tests { use super::*; + fn make_versions<'a>(versions: impl Iterator) -> Versions { + let map = versions + .into_iter() + .map(|(name, version, publish)| { + let publish = *publish; + ( + name.to_string(), + VersionWithMetadata { + version: Version::parse(&version).unwrap(), + publish, + }, + ) + }) + .collect::>(); + + Versions(map) + } + + #[test] + fn unpublished_deps_cant_be_deps() { + let manifest = br#" + [package] + name = "test" + version = "1.2.0" + + [build-dependencies] + build_something = "1.3" + local_build_something = { path = "../local_build_something", version = "0.4.0-different" } + + [dev-dependencies] + dev_something = "1.1" + local_dev_something = { path = "../local_dev_something" } + + [dependencies] + something = "1.0" + local_something = { path = "../local_something" } + "#; + let metadata = toml::from_slice(manifest).unwrap(); + let mut manifest = Manifest { + path: "test".into(), + metadata, + }; + let versions = &[ + ("local_build_something", "0.2.0", true), + ("local_dev_something", "0.1.0", false), + ("local_something", "1.1.3", false), + ]; + let versions = make_versions(versions.iter()); + fix_manifest(&versions, &mut manifest).expect_err("depends on unpublished local something"); + set_publish_false(&manifest.path, &mut manifest.metadata, false).unwrap(); + fix_manifest(&versions, &mut manifest) + .expect("now it will work, the crate isn't published"); + } + #[test] fn test_fix_dep_sets() { let manifest = br#" @@ -273,16 +396,14 @@ mod tests { path: "test".into(), metadata, }; - let versions = vec![ - ("local_build_something", "0.2.0"), - ("local_dev_something", "0.1.0"), - ("local_something", "1.1.3"), - ] - .into_iter() - .map(|e| (e.0.to_string(), Version::parse(e.1).unwrap())) - .collect(); - - fix_dep_sets(&versions, &mut manifest.metadata).expect("success"); + let versions = &[ + ("local_build_something", "0.2.0", true), + ("local_dev_something", "0.1.0", false), + ("local_something", "1.1.3", true), + ]; + let versions = make_versions(versions.iter()); + + fix_dep_sets(&versions.published(), &mut manifest.metadata).expect("success"); let actual_deps = &manifest.metadata["dependencies"]; assert_eq!( diff --git a/tools/ci-build/publisher/src/subcommand/fix_manifests/validate.rs b/tools/ci-build/publisher/src/subcommand/fix_manifests/validate.rs index 53202468f3..40aa8f609a 100644 --- a/tools/ci-build/publisher/src/subcommand/fix_manifests/validate.rs +++ b/tools/ci-build/publisher/src/subcommand/fix_manifests/validate.rs @@ -5,10 +5,10 @@ use crate::fs::Fs; use crate::package::discover_and_validate_package_batches; +use crate::subcommand::fix_manifests::Versions; use anyhow::{anyhow, bail, Result}; use semver::Version; use smithy_rs_tool_common::package::PackageCategory; -use std::collections::BTreeMap; use std::path::Path; use tracing::info; @@ -17,7 +17,7 @@ use tracing::info; /// For now, this validates: /// - `aws-smithy-` prefixed versions match `aws-` (NOT `aws-sdk-`) prefixed versions pub(super) fn validate_before_fixes( - versions: &BTreeMap, + versions: &Versions, disable_version_number_validation: bool, ) -> Result<()> { // Later when we only generate independently versioned SDK crates, this flag can become permanent. @@ -30,7 +30,7 @@ pub(super) fn validate_before_fixes( .get("aws-smithy-types") .ok_or_else(|| anyhow!("`aws-smithy-types` crate missing"))?; - for (name, version) in versions { + for (name, version) in versions.published_crates() { let category = PackageCategory::from_package_name(name); if category == PackageCategory::SmithyRuntime || category == PackageCategory::AwsRuntime { confirm_version(name, expected_runtime_version, version)?; @@ -63,14 +63,22 @@ pub(super) async fn validate_after_fixes(location: &Path) -> Result<()> { #[cfg(test)] mod test { use super::*; + use crate::subcommand::fix_manifests::VersionWithMetadata; + use std::collections::BTreeMap; use std::str::FromStr; - fn versions(versions: &[(&'static str, &'static str)]) -> BTreeMap { + fn versions(versions: &[(&'static str, &'static str)]) -> Versions { let mut map = BTreeMap::new(); for (name, version) in versions { - map.insert((*name).into(), Version::from_str(version).unwrap()); + map.insert( + (*name).into(), + VersionWithMetadata { + version: Version::from_str(version).unwrap(), + publish: true, + }, + ); } - map + Versions(map) } #[track_caller] diff --git a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs index e06c79c6fa..6b9ac35397 100644 --- a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs +++ b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs @@ -4,7 +4,7 @@ */ use crate::fs::Fs; -use crate::package::{discover_package_manifests, read_packages}; +use crate::package::discover_packages; use anyhow::{bail, Context, Result}; use clap::Parser; use semver::Version; @@ -26,9 +26,15 @@ pub struct GenerateVersionManifestArgs { /// Revision of `aws-doc-sdk-examples` repository used to retrieve examples #[clap(long)] examples_revision: String, + /// Same as `input_location` but kept for backwards compatibility + #[clap(long, required_unless_present = "input-location")] + location: Option, /// Path containing the generated SDK to generate a version manifest for - #[clap(long)] - location: PathBuf, + #[clap(long, required_unless_present = "location")] + input_location: Option, + /// Path to a directory in which a version manifest is generated + #[clap(long, required_unless_present = "location")] + output_location: Option, /// Optional path to the `versions.toml` manifest from the previous SDK release #[clap(long)] previous_release_versions: Option, @@ -39,6 +45,8 @@ pub async fn subcommand_generate_version_manifest( smithy_build, examples_revision, location, + input_location, + output_location, previous_release_versions, .. }: &GenerateVersionManifestArgs, @@ -52,10 +60,17 @@ pub async fn subcommand_generate_version_manifest( info!("Resolved smithy-rs revision to {}", smithy_rs_revision); let smithy_build_root = SmithyBuildRoot::from_file(smithy_build)?; - let manifests = discover_package_manifests(location.into()) - .await - .context("discover package manifests")?; - let packages = read_packages(Fs::Real, manifests) + let input_location = match (location, input_location) { + (Some(location), None) => location, + (None, Some(input_location)) => input_location, + _ => bail!("Only one of `--location` or `--input-location` should be provided"), + }; + let output_location = match (location, output_location) { + (Some(location), None) => location, + (None, Some(output_location)) => output_location, + _ => bail!("Only one of `--location` or `--output-location` should be provided"), + }; + let packages = discover_packages(Fs::Real, input_location.into()) .await .context("read packages")?; @@ -99,7 +114,7 @@ pub async fn subcommand_generate_version_manifest( }; versions_manifest.release = generate_release_metadata(&versions_manifest, previous_release_versions)?; - let manifest_file_name = location.join("versions.toml"); + let manifest_file_name = output_location.join("versions.toml"); info!("Writing {:?}...", manifest_file_name); versions_manifest.write_to_file(&manifest_file_name)?; Ok(()) diff --git a/tools/ci-build/sanity-test b/tools/ci-build/sanity-test index 933d36c9a3..329bc6558a 100755 --- a/tools/ci-build/sanity-test +++ b/tools/ci-build/sanity-test @@ -18,3 +18,4 @@ rustc +"${RUST_NIGHTLY_VERSION}" --version rustc --version sdk-versioner --version cargo semver-checks --version +mdbook --version diff --git a/tools/ci-build/sdk-lints/Cargo.lock b/tools/ci-build/sdk-lints/Cargo.lock index 4d1a68488a..a998177790 100644 --- a/tools/ci-build/sdk-lints/Cargo.lock +++ b/tools/ci-build/sdk-lints/Cargo.lock @@ -2,30 +2,45 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -45,11 +60,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.13.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -57,17 +87,23 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cargo_toml" @@ -82,9 +118,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -99,7 +135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -119,7 +155,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -143,28 +179,46 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] [[package]] -name = "fastrand" -version = "1.8.0" +name = "errno" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ - "instant", + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fnv" version = "1.0.7" @@ -188,45 +242,45 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -234,11 +288,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.14" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -261,9 +321,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -276,9 +336,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -310,9 +370,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -347,9 +407,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -357,40 +417,31 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" -version = "2.5.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -403,18 +454,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -424,27 +478,35 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", "windows-sys", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -458,19 +520,28 @@ dependencies = [ "tempfile", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.15.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -481,13 +552,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -498,11 +569,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -511,21 +581,21 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -535,9 +605,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro-error" @@ -548,7 +618,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -565,62 +635,65 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] -name = "regex-syntax" -version = "0.6.27" +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -653,19 +726,37 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "lazy_static", "windows-sys", ] @@ -684,11 +775,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -697,9 +788,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -707,35 +798,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -756,9 +847,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -781,9 +872,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -797,9 +888,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -808,32 +910,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "tinyvec" @@ -846,31 +947,31 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "pin-project-lite", "socket2", - "winapi", + "windows-sys", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -878,9 +979,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -892,9 +993,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "indexmap", "serde", @@ -908,9 +1009,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", @@ -920,41 +1021,41 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -967,9 +1068,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -990,11 +1091,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1006,9 +1106,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1016,24 +1116,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1043,9 +1143,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1053,28 +1153,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1113,46 +1213,69 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" diff --git a/tools/ci-build/sdk-lints/Cargo.toml b/tools/ci-build/sdk-lints/Cargo.toml index 394c12e68d..99a567c13a 100644 --- a/tools/ci-build/sdk-lints/Cargo.toml +++ b/tools/ci-build/sdk-lints/Cargo.toml @@ -14,7 +14,7 @@ opt-level = 0 anyhow = "1" cargo_toml = "0.10.1" clap = { version = "~3.1.18", features = ["derive"]} -toml = "0.5.8" -serde = { version = "1", features = ["derive"]} lazy_static = "1.4.0" +serde = { version = "1", features = ["derive"]} smithy-rs-tool-common = { path = "../smithy-rs-tool-common" } +toml = "0.5.8" diff --git a/tools/ci-build/sdk-lints/src/lint_cargo_toml.rs b/tools/ci-build/sdk-lints/src/lint_cargo_toml.rs index e683459cab..2bb7610c5a 100644 --- a/tools/ci-build/sdk-lints/src/lint_cargo_toml.rs +++ b/tools/ci-build/sdk-lints/src/lint_cargo_toml.rs @@ -53,7 +53,11 @@ struct Metadata { const RUST_SDK_TEAM: &str = "AWS Rust SDK Team "; const SERVER_TEAM: &str = "Smithy Rust Server "; -const SERVER_CRATES: &[&str] = &["aws-smithy-http-server", "aws-smithy-http-server-python"]; +const SERVER_CRATES: &[&str] = &[ + "aws-smithy-http-server", + "aws-smithy-http-server-python", + "aws-smithy-http-server-typescript", +]; /// Check crate licensing /// diff --git a/tools/ci-build/sdk-versioner/Cargo.lock b/tools/ci-build/sdk-versioner/Cargo.lock index 2862d9eeb4..0ff7ddce42 100644 --- a/tools/ci-build/sdk-versioner/Cargo.lock +++ b/tools/ci-build/sdk-versioner/Cargo.lock @@ -2,30 +2,45 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -45,11 +60,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.13.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -57,23 +87,29 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -88,10 +124,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", - "indexmap", + "indexmap 1.9.3", "lazy_static", "strsim", "termcolor", @@ -108,7 +144,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -132,19 +168,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "ctor" -version = "0.1.23" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" -dependencies = [ - "quote", - "syn", -] +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "diff" @@ -154,22 +180,46 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] [[package]] -name = "fastrand" -version = "1.8.0" +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ - "instant", + "errno-dragonfly", + "libc", + "windows-sys", ] +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fnv" version = "1.0.7" @@ -193,45 +243,45 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -239,11 +289,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.14" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -251,7 +307,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -264,11 +320,17 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -281,9 +343,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -315,9 +377,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -352,9 +414,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -362,40 +424,41 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] -name = "instant" -version = "0.1.12" +name = "indexmap" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "cfg-if", + "equivalent", + "hashbrown 0.14.0", ] [[package]] name = "ipnet" -version = "2.5.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -408,18 +471,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -429,27 +495,35 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", "windows-sys", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -463,19 +537,28 @@ dependencies = [ "tempfile", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.15.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -486,13 +569,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -503,11 +586,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -516,30 +598,27 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] -name = "output_vt100" -version = "0.1.3" +name = "pathdiff" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -549,19 +628,17 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -574,7 +651,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -591,62 +668,65 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] -name = "regex-syntax" -version = "0.6.27" +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -679,19 +759,37 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "lazy_static", "windows-sys", ] @@ -701,6 +799,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "pathdiff", "pretty_assertions", "smithy-rs-tool-common", "tempfile", @@ -709,11 +808,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -722,9 +821,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -732,35 +831,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -781,9 +880,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -806,9 +905,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -822,9 +921,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -833,32 +943,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "tinyvec" @@ -871,31 +980,31 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "pin-project-lite", "socket2", - "winapi", + "windows-sys", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -903,9 +1012,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -917,27 +1026,27 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "indexmap", + "indexmap 1.9.3", "serde", ] [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.6" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08de71aa0d6e348f070457f85af8bd566e2bc452156a423ddf22861b3a953fae" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", + "indexmap 2.0.0", "toml_datetime", "winnow", ] @@ -950,9 +1059,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", @@ -962,41 +1071,41 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -1009,9 +1118,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1032,11 +1141,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1048,9 +1156,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1058,24 +1166,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1085,9 +1193,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1095,28 +1203,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1155,52 +1263,75 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.3.5" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" dependencies = [ "memchr", ] diff --git a/tools/ci-build/sdk-versioner/Cargo.toml b/tools/ci-build/sdk-versioner/Cargo.toml index f7fc947722..ee732cc430 100644 --- a/tools/ci-build/sdk-versioner/Cargo.toml +++ b/tools/ci-build/sdk-versioner/Cargo.toml @@ -15,8 +15,9 @@ opt-level = 0 [dependencies] anyhow = "1.0" clap = { version = "~3.1.18", features = ["derive"] } -toml_edit = { version = "0.19.6" } +pathdiff = "0.2.1" smithy-rs-tool-common = { version = "0.1", path = "../smithy-rs-tool-common" } +toml_edit = { version = "0.19.6" } [dev-dependencies] pretty_assertions = "1.3" diff --git a/tools/ci-build/sdk-versioner/src/main.rs b/tools/ci-build/sdk-versioner/src/main.rs index 77cf9ff606..63ffd6bd2f 100644 --- a/tools/ci-build/sdk-versioner/src/main.rs +++ b/tools/ci-build/sdk-versioner/src/main.rs @@ -29,6 +29,7 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + // TODO(https://github.com/awslabs/smithy-rs/issues/2810): `isolate_crates` can be removed once the `Flat` example directory structure is cleaned up /// Makes each individual crate its own workspace #[clap(long)] isolate_crates: bool, @@ -41,6 +42,7 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + // TODO(https://github.com/awslabs/smithy-rs/issues/2810): `isolate_crates` can be removed once the `Flat` example directory structure is cleaned up /// Makes each individual crate its own workspace #[clap(long)] isolate_crates: bool, @@ -56,6 +58,7 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + // TODO(https://github.com/awslabs/smithy-rs/issues/2810): `isolate_crates` can be removed once the `Flat` example directory structure is cleaned up /// Makes each individual crate its own workspace #[clap(long)] isolate_crates: bool, @@ -87,8 +90,26 @@ impl Args { } } -struct DependencyContext<'a> { - sdk_path: Option<&'a Path>, +// TODO(https://github.com/awslabs/smithy-rs/issues/2810): Remove `SdkPath` and just use a `PathBuf` with the new logic +// This is only around for backwards compatibility for the next release's sync process0 +enum SdkPath { + /// Don't even attempt to resolve the correct relative path to dependencies + UseDumbLogic(PathBuf), + /// Resolve the correct relative path to dependencies + UseNewLogic(PathBuf), +} +impl From<&PathBuf> for SdkPath { + fn from(value: &PathBuf) -> Self { + if !value.exists() { + SdkPath::UseDumbLogic(value.into()) + } else { + SdkPath::UseNewLogic(value.into()) + } + } +} + +struct DependencyContext { + sdk_path: Option, versions_manifest: Option, } @@ -96,7 +117,7 @@ fn main() -> Result<()> { let args = Args::parse().validate()?; let dependency_context = match &args { Args::UsePathDependencies { sdk_path, .. } => DependencyContext { - sdk_path: Some(sdk_path), + sdk_path: Some(sdk_path.into()), versions_manifest: None, }, Args::UseVersionDependencies { versions_toml, .. } => DependencyContext { @@ -108,7 +129,7 @@ fn main() -> Result<()> { versions_toml, .. } => DependencyContext { - sdk_path: Some(sdk_path), + sdk_path: Some(sdk_path.into()), versions_manifest: Some(VersionsManifest::from_file(versions_toml)?), }, }; @@ -133,6 +154,7 @@ fn update_manifest( isolate_crates: bool, ) -> anyhow::Result<()> { println!("Updating {:?}...", manifest_path); + let crate_path = manifest_path.parent().expect("manifest has a parent"); let mut metadata: Document = String::from_utf8( fs::read(manifest_path).with_context(|| format!("failed to read {manifest_path:?}"))?, @@ -150,9 +172,11 @@ fn update_manifest( manifest_path ); } - changed = - update_dependencies(dependencies.as_table_mut().unwrap(), dependency_context)? - || changed; + changed = update_dependencies( + dependencies.as_table_mut().unwrap(), + dependency_context, + crate_path, + )? || changed; } } if isolate_crates && !metadata.contains_key("workspace") { @@ -177,6 +201,7 @@ fn update_manifest( fn update_dependencies( dependencies: &mut Table, dependency_context: &DependencyContext, + crate_path: &Path, ) -> Result { let mut changed = false; for (key, value) in dependencies.iter_mut() { @@ -191,6 +216,7 @@ fn update_dependencies( key.get(), old_value, dependency_context, + crate_path, )?)); changed = true; } @@ -210,10 +236,14 @@ fn crate_path_name(name: &str) -> &str { } fn updated_dependency_value( - crate_name: &str, + dependency_name: &str, old_value: Table, dependency_context: &DependencyContext, + crate_path: &Path, ) -> Result { + let crate_path = crate_path + .canonicalize() + .context("failed to canonicalize crate path")?; let mut value = old_value; // Remove keys that will be replaced @@ -223,25 +253,45 @@ fn updated_dependency_value( value.remove("path"); // Set the `path` if one was given - if let Some(path) = &dependency_context.sdk_path { - let crate_path = path.join(crate_path_name(crate_name)); - value["path"] = toml_edit::value( - crate_path - .as_os_str() - .to_str() - .expect("valid utf-8 path") - .to_string(), - ); + match &dependency_context.sdk_path { + Some(SdkPath::UseDumbLogic(sdk_path)) => { + let crate_path = sdk_path.join(crate_path_name(dependency_name)); + value["path"] = toml_edit::value( + crate_path + .as_os_str() + .to_str() + .expect("valid utf-8 path") + .to_string(), + ); + } + Some(SdkPath::UseNewLogic(sdk_path)) => { + let dependency_path = sdk_path + .join(crate_path_name(dependency_name)) + .canonicalize() + .context("failed to canonicalize dependency path")?; + if let Some(relative_path) = pathdiff::diff_paths(&dependency_path, &crate_path) { + value["path"] = toml_edit::value( + relative_path + .as_os_str() + .to_str() + .expect("valid utf-8 path") + .to_string(), + ); + } else { + bail!("Failed to create relative path from {crate_path:?} to {dependency_path:?}"); + } + } + _ => {} } // Set the `version` if one was given if let Some(manifest) = &dependency_context.versions_manifest { - if let Some(crate_metadata) = manifest.crates.get(crate_name) { + if let Some(crate_metadata) = manifest.crates.get(dependency_name) { value["version"] = toml_edit::value(crate_metadata.version.clone()); } else { bail!( "Crate `{}` was missing from the `versions.toml`", - crate_name + dependency_name ); } } @@ -270,11 +320,12 @@ fn discover_manifests(manifests: &mut Vec, path: impl AsRef) -> a #[cfg(test)] mod tests { - use crate::{update_manifest, DependencyContext}; + use crate::{crate_path_name, update_manifest, DependencyContext, SdkPath}; use pretty_assertions::assert_eq; use smithy_rs_tool_common::package::PackageCategory; use smithy_rs_tool_common::versions_manifest::{CrateVersion, VersionsManifest}; use std::path::PathBuf; + use std::{fs, process}; fn versions_toml_for(crates: &[(&str, &str)]) -> VersionsManifest { VersionsManifest { @@ -321,12 +372,47 @@ features = ["foo", "baz"] "#; #[track_caller] - fn test_with_context(isolate_crates: bool, context: DependencyContext, expected: &[u8]) { - let manifest_file = tempfile::NamedTempFile::new().unwrap(); - let manifest_path = manifest_file.into_temp_path(); + fn test_with_context( + isolate_crates: bool, + crate_path_rel: &str, + sdk_crates: &[&'static str], + context: DependencyContext, + expected: &[u8], + ) { + let temp_dir = tempfile::tempdir().unwrap(); + let crate_path = temp_dir.path().join(crate_path_rel); + fs::create_dir_all(&crate_path).unwrap(); + + let manifest_path = crate_path.join("Cargo.toml"); std::fs::write(&manifest_path, TEST_MANIFEST).unwrap(); - update_manifest(&manifest_path, &context, isolate_crates).expect("success"); + if let Some(SdkPath::UseNewLogic(sdk_path)) = context.sdk_path.as_ref() { + for sdk_crate in sdk_crates { + let sdk_crate_path = temp_dir + .path() + .join(sdk_path) + .join(crate_path_name(sdk_crate)); + fs::create_dir_all(sdk_crate_path).unwrap(); + } + } + // Assist with debugging when the tests fail + if let Ok(output) = process::Command::new("find").arg(temp_dir.path()).output() { + println!( + "Test directory structure:\n{}", + String::from_utf8_lossy(&output.stdout) + ); + } + + let fixed_context = if let Some(SdkPath::UseNewLogic(sdk_path)) = context.sdk_path.as_ref() + { + DependencyContext { + sdk_path: Some(SdkPath::UseNewLogic(temp_dir.path().join(sdk_path))), + versions_manifest: context.versions_manifest, + } + } else { + context + }; + update_manifest(&manifest_path, &fixed_context, isolate_crates).expect("success"); let actual = String::from_utf8(std::fs::read(&manifest_path).expect("read tmp file")).unwrap(); @@ -338,6 +424,8 @@ features = ["foo", "baz"] fn update_dependencies_with_versions() { test_with_context( false, + "examples/foo", + &[], DependencyContext { sdk_path: None, versions_manifest: Some(versions_toml_for(&[ @@ -374,8 +462,54 @@ features = ["foo", "baz"] fn update_dependencies_with_paths() { test_with_context( false, + "path/to/test", + &[ + "aws-config", + "aws-sdk-s3", + "aws-smithy-types", + "aws-smithy-http", + ], + DependencyContext { + sdk_path: Some(SdkPath::UseNewLogic(PathBuf::from("sdk"))), + versions_manifest: None, + }, + br#" +[package] +name = "test" +version = "0.1.0" + +# Some comment that should be preserved +[dependencies] +aws-config = { path = "../../../sdk/aws-config" } +aws-sdk-s3 = { path = "../../../sdk/s3" } +aws-smithy-types = { path = "../../../sdk/aws-smithy-types" } +aws-smithy-http = { path = "../../../sdk/aws-smithy-http", features = ["test-util"] } +something-else = { version = "0.1", no-default-features = true } +tokio = { version = "1.18", features = ["net"] } + +[dev-dependencies.another-thing] +# some comment +version = "5.0" +# another comment +features = ["foo", "baz"] +"#, + ); + } + + // TODO(https://github.com/awslabs/smithy-rs/issues/2810): Remove this test + #[test] + fn update_dependencies_with_paths_dumb_logic() { + test_with_context( + false, + "path/to/test", + &[ + "aws-config", + "aws-sdk-s3", + "aws-smithy-types", + "aws-smithy-http", + ], DependencyContext { - sdk_path: Some(&PathBuf::from("/foo/asdf/")), + sdk_path: Some(SdkPath::UseDumbLogic(PathBuf::from("a/dumb/path/to"))), versions_manifest: None, }, br#" @@ -385,10 +519,10 @@ version = "0.1.0" # Some comment that should be preserved [dependencies] -aws-config = { path = "/foo/asdf/aws-config" } -aws-sdk-s3 = { path = "/foo/asdf/s3" } -aws-smithy-types = { path = "/foo/asdf/aws-smithy-types" } -aws-smithy-http = { path = "/foo/asdf/aws-smithy-http", features = ["test-util"] } +aws-config = { path = "a/dumb/path/to/aws-config" } +aws-sdk-s3 = { path = "a/dumb/path/to/s3" } +aws-smithy-types = { path = "a/dumb/path/to/aws-smithy-types" } +aws-smithy-http = { path = "a/dumb/path/to/aws-smithy-http", features = ["test-util"] } something-else = { version = "0.1", no-default-features = true } tokio = { version = "1.18", features = ["net"] } @@ -405,8 +539,15 @@ features = ["foo", "baz"] fn update_dependencies_with_versions_and_paths() { test_with_context( false, + "deep/path/to/test", + &[ + "aws-config", + "aws-sdk-s3", + "aws-smithy-types", + "aws-smithy-http", + ], DependencyContext { - sdk_path: Some(&PathBuf::from("/foo/asdf/")), + sdk_path: Some(SdkPath::UseNewLogic(PathBuf::from("sdk"))), versions_manifest: Some(versions_toml_for(&[ ("aws-config", "0.5.0"), ("aws-sdk-s3", "0.13.0"), @@ -421,10 +562,10 @@ version = "0.1.0" # Some comment that should be preserved [dependencies] -aws-config = { version = "0.5.0", path = "/foo/asdf/aws-config" } -aws-sdk-s3 = { version = "0.13.0", path = "/foo/asdf/s3" } -aws-smithy-types = { version = "0.10.0", path = "/foo/asdf/aws-smithy-types" } -aws-smithy-http = { version = "0.9.0", path = "/foo/asdf/aws-smithy-http", features = ["test-util"] } +aws-config = { version = "0.5.0", path = "../../../../sdk/aws-config" } +aws-sdk-s3 = { version = "0.13.0", path = "../../../../sdk/s3" } +aws-smithy-types = { version = "0.10.0", path = "../../../../sdk/aws-smithy-types" } +aws-smithy-http = { version = "0.9.0", path = "../../../../sdk/aws-smithy-http", features = ["test-util"] } something-else = { version = "0.1", no-default-features = true } tokio = { version = "1.18", features = ["net"] } @@ -441,8 +582,15 @@ features = ["foo", "baz"] fn update_dependencies_isolate_crates() { test_with_context( true, + "deep/path/to/test", + &[ + "aws-config", + "aws-sdk-s3", + "aws-smithy-types", + "aws-smithy-http", + ], DependencyContext { - sdk_path: Some(&PathBuf::from("/foo/asdf/")), + sdk_path: Some(SdkPath::UseNewLogic(PathBuf::from("sdk"))), versions_manifest: Some(versions_toml_for(&[ ("aws-config", "0.5.0"), ("aws-sdk-s3", "0.13.0"), @@ -459,10 +607,10 @@ version = "0.1.0" # Some comment that should be preserved [dependencies] -aws-config = { version = "0.5.0", path = "/foo/asdf/aws-config" } -aws-sdk-s3 = { version = "0.13.0", path = "/foo/asdf/s3" } -aws-smithy-types = { version = "0.10.0", path = "/foo/asdf/aws-smithy-types" } -aws-smithy-http = { version = "0.9.0", path = "/foo/asdf/aws-smithy-http", features = ["test-util"] } +aws-config = { version = "0.5.0", path = "../../../../sdk/aws-config" } +aws-sdk-s3 = { version = "0.13.0", path = "../../../../sdk/s3" } +aws-smithy-types = { version = "0.10.0", path = "../../../../sdk/aws-smithy-types" } +aws-smithy-http = { version = "0.9.0", path = "../../../../sdk/aws-smithy-http", features = ["test-util"] } something-else = { version = "0.1", no-default-features = true } tokio = { version = "1.18", features = ["net"] } diff --git a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs index 4944c7aa4c..5f358be99d 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs @@ -11,22 +11,17 @@ use std::fmt::{self, Display}; use std::path::Path; use std::str::FromStr; -#[derive(Copy, Clone, Debug, Serialize, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Default, Serialize, PartialEq, Eq)] pub enum SdkAffected { #[serde(rename = "client")] Client, #[serde(rename = "server")] Server, #[serde(rename = "all")] + #[default] All, } -impl Default for SdkAffected { - fn default() -> Self { - SdkAffected::All - } -} - impl Display for SdkAffected { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -154,6 +149,13 @@ impl HandAuthoredEntry { if self.references.is_empty() { bail!("Changelog entry must refer to at least one pull request or issue"); } + if self.message.len() > 800 { + bail!( + "Your changelog entry is too long. Post long-form change log entries in \ + the GitHub Discussions under the Changelog category, and link to them from \ + the changelog." + ); + } Ok(()) } @@ -209,7 +211,6 @@ impl Changelog { // Remove comments from the top let value = value .split('\n') - .into_iter() .filter(|line| !line.trim().starts_with('#')) .collect::>() .join("\n"); diff --git a/tools/ci-build/smithy-rs-tool-common/src/git.rs b/tools/ci-build/smithy-rs-tool-common/src/git.rs index b1b34aa263..da57a95f5a 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/git.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/git.rs @@ -104,11 +104,11 @@ pub trait Git: Send + Sync { /// Returns a list of commit hashes in reverse chronological order starting with /// `start_inclusive_revision` and ending before `end_exclusive_revision`. Both of /// these arguments can be any kind of Git revision (e.g., HEAD, HEAD~2, commit hash, etc). - fn rev_list<'a>( + fn rev_list( &self, start_inclusive_revision: &str, end_exclusive_revision: &str, - path: Option<&'a Path>, + path: Option<&Path>, ) -> Result>; /// Returns information about a given revision. @@ -295,7 +295,6 @@ impl Git for GitCLI { let (stdout, _) = output_text(&output); Ok(stdout .split_ascii_whitespace() - .into_iter() .map(CommitHash::from) .collect()) } diff --git a/tools/ci-cdk/canary-runner/Cargo.lock b/tools/ci-cdk/canary-runner/Cargo.lock index adccd1cddc..6edc67f2c4 100644 --- a/tools/ci-cdk/canary-runner/Cargo.lock +++ b/tools/ci-cdk/canary-runner/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -10,13 +19,19 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -28,30 +43,30 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-recursion" -version = "0.3.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" +checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -73,10 +88,11 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aws-config" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a3ad9e793335d75b2d2faad583487efcc0df9154aff06f299a5c1fc8795698" +checksum = "bcdcf0d683fe9c23d32cf5b53c9918ea0a500375a9fb20109802552658e576c9" dependencies = [ + "aws-credential-types", "aws-http", "aws-sdk-sso", "aws-sdk-sts", @@ -88,24 +104,40 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", + "fastrand 1.9.0", "hex", "http", "hyper", "ring", - "time 0.3.19", + "time", "tokio", "tower", "tracing", "zeroize", ] +[[package]] +name = "aws-credential-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcdb2f7acbc076ff5ad05e7864bdb191ca70a6fd07668dc3a1a8bcd051de5ae" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "fastrand 1.9.0", + "tokio", + "tracing", + "zeroize", +] + [[package]] name = "aws-endpoint" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd4e9dad553017821ee529f186e033700e8d61dd5c4b60066b4d8fe805b8cfc" +checksum = "8cce1c41a6cfaa726adee9ebb9a56fcd2bbfd8be49fd8a04c5e20fd968330b04" dependencies = [ "aws-smithy-http", + "aws-smithy-types", "aws-types", "http", "regex", @@ -114,10 +146,11 @@ dependencies = [ [[package]] name = "aws-http" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ef5a579a51d352b628b76f4855ba716be686305e5e59970c476d1ae2214e90d" +checksum = "aadbc44e7a8f3e71c8b374e03ecd972869eb91dd2bc89ed018954a52ba84bc44" dependencies = [ + "aws-credential-types", "aws-smithy-http", "aws-smithy-types", "aws-types", @@ -132,10 +165,11 @@ dependencies = [ [[package]] name = "aws-sdk-cloudwatch" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5b3d07682c55fa19d9006ef6c4739ea59d8410955b67449bf208c61ffdf834" +checksum = "7263a92103fcd82ad1cb882274ece03a84a1041d08a487bb3d783c60acdf8f49" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -143,22 +177,26 @@ dependencies = [ "aws-smithy-client", "aws-smithy-http", "aws-smithy-http-tower", + "aws-smithy-json", "aws-smithy-query", "aws-smithy-types", "aws-smithy-xml", "aws-types", "bytes", "http", + "regex", "tokio-stream", "tower", + "tracing", ] [[package]] name = "aws-sdk-lambda" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f4cd6001d23ff50c00caf6079e1666060215914f8801a49f614252bc04883d" +checksum = "b3ad176ffaa3aafa532246eb6a9f18a7d68da19950704ecc95d33d9dc3c62a9b" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -171,16 +209,19 @@ dependencies = [ "aws-types", "bytes", "http", + "regex", "tokio-stream", "tower", + "tracing", ] [[package]] name = "aws-sdk-s3" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2c19b69297f16b3f18936e363f954e7504c23a4a0dc3f2833712313c09c2aa" +checksum = "fba197193cbb4bcb6aad8d99796b2291f36fa89562ded5d4501363055b0de89f" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -191,24 +232,29 @@ dependencies = [ "aws-smithy-eventstream", "aws-smithy-http", "aws-smithy-http-tower", + "aws-smithy-json", "aws-smithy-types", "aws-smithy-xml", "aws-types", "bytes", - "bytes-utils", "http", "http-body", + "once_cell", + "percent-encoding", + "regex", "tokio-stream", "tower", "tracing", + "url", ] [[package]] name = "aws-sdk-sso" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f014b8ad3178b414bf732b36741325ef659fc40752f8c292400fb7c4ecb7fdd0" +checksum = "c8b812340d86d4a766b2ca73f740dfd47a97c2dff0c06c8517a16d88241957e4" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -221,16 +267,19 @@ dependencies = [ "aws-types", "bytes", "http", + "regex", "tokio-stream", "tower", + "tracing", ] [[package]] name = "aws-sdk-sts" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37e45fdce84327c69fb924b9188fd889056c6afafbd494e8dd0daa400f9c082" +checksum = "265fac131fbfc188e5c3d96652ea90ecc676a934e3174eaaee523c6cec040b3b" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -238,21 +287,25 @@ dependencies = [ "aws-smithy-client", "aws-smithy-http", "aws-smithy-http-tower", + "aws-smithy-json", "aws-smithy-query", "aws-smithy-types", "aws-smithy-xml", "aws-types", "bytes", "http", + "regex", "tower", + "tracing", ] [[package]] name = "aws-sig-auth" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6530e72945c11439e9b3c423c95a656a233d73c3a7d4acaf9789048e1bdf7da7" +checksum = "3b94acb10af0c879ecd5c7bdf51cda6679a0a4f4643ce630905a77673bfa3c61" dependencies = [ + "aws-credential-types", "aws-sigv4", "aws-smithy-eventstream", "aws-smithy-http", @@ -263,29 +316,30 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6351c3ba468b04bd819f64ea53538f5f53e3d6b366b27deabee41e73c9edb3af" +checksum = "9d2ce6f507be68e968a33485ced670111d1cbad161ddbbab1e313c03d37d8f4c" dependencies = [ "aws-smithy-eventstream", "aws-smithy-http", "bytes", "form_urlencoded", "hex", + "hmac", "http", "once_cell", "percent-encoding", "regex", - "ring", - "time 0.3.19", + "sha2", + "time", "tracing", ] [[package]] name = "aws-smithy-async" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fc23ad8d050c241bdbfa74ae360be94a844ace8e218f64a2b2de77bfa9a707" +checksum = "13bda3996044c202d75b91afeb11a9afae9db9a721c6a7a427410018e286b880" dependencies = [ "futures-util", "pin-project-lite", @@ -295,9 +349,9 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd674df030b337a84eb67539db048676c691d9c88f0c54cf7748da11836cfd8" +checksum = "07ed8b96d95402f3f6b8b57eb4e0e45ee365f78b1a924faf20ff6e97abf1eae6" dependencies = [ "aws-smithy-http", "aws-smithy-types", @@ -316,22 +370,23 @@ dependencies = [ [[package]] name = "aws-smithy-client" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e147b157f49ce77f2a86ec693a14c84b2441fa28be58ffb2febb77d5726c934" +checksum = "0a86aa6e21e86c4252ad6a0e3e74da9617295d8d6e374d552be7d3059c41cedd" dependencies = [ "aws-smithy-async", "aws-smithy-http", "aws-smithy-http-tower", "aws-smithy-types", "bytes", - "fastrand", + "fastrand 1.9.0", "http", "http-body", "hyper", - "hyper-rustls 0.22.1", + "hyper-rustls 0.23.2", "lazy_static", "pin-project-lite", + "rustls 0.20.8", "tokio", "tower", "tracing", @@ -339,9 +394,9 @@ dependencies = [ [[package]] name = "aws-smithy-eventstream" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da29e67a0b90a2bc5f2bd0a06fd43e728de62e02048879c15f646a3edf8db012" +checksum = "460c8da5110835e3d9a717c61f5556b20d03c32a1dec57f8fc559b360f733bb8" dependencies = [ "aws-smithy-types", "bytes", @@ -350,9 +405,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc1af50eac644ab6f58e5bae29328ba3092851fc2ce648ad139134699b2b66f" +checksum = "2b3b693869133551f135e1f2c77cb0b8277d9e3e17feaf2213f735857c4f0d28" dependencies = [ "aws-smithy-eventstream", "aws-smithy-types", @@ -365,6 +420,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "pin-utils", "tokio", "tokio-util", "tracing", @@ -372,11 +428,12 @@ dependencies = [ [[package]] name = "aws-smithy-http-tower" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bf4c4664dff2febf91f8796505c5bc8f38a0bff0d1397d1d3fdda17bd5c5d1" +checksum = "3ae4f6c5798a247fac98a867698197d9ac22643596dc3777f0c76b91917616b9" dependencies = [ "aws-smithy-http", + "aws-smithy-types", "bytes", "http", "http-body", @@ -387,18 +444,18 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e6ebc76c3c108dd2a96506bf47dc31f75420811a19f1a09907524d1451789d2" +checksum = "23f9f42fbfa96d095194a632fbac19f60077748eba536eb0b9fecc28659807f8" dependencies = [ "aws-smithy-types", ] [[package]] name = "aws-smithy-query" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2956f1385c4daa883907a2c81d32256af8f95834c9de1bc0613fa68db63b88c4" +checksum = "98819eb0b04020a1c791903533b638534ae6c12e2aceda3e6e6fba015608d51d" dependencies = [ "aws-smithy-types", "urlencoding", @@ -406,31 +463,33 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352fb335ec1d57160a17a13e87aaa0a172ab780ddf58bfc85caedd3b7e47caed" +checksum = "16a3d0bf4f324f4ef9793b86a1701d9700fbcdbd12a846da45eed104c634c6e8" dependencies = [ + "base64-simd", "itoa", "num-integer", "ryu", - "time 0.3.19", + "time", ] [[package]] name = "aws-smithy-xml" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cf2807fa715a5a3296feffb06ce45252bd0dfd48f52838128c48fb339ddbf5c" +checksum = "b1b9d12875731bd07e767be7baad95700c3137b56730ec9ddeedb52a5e5ca63b" dependencies = [ "xmlparser", ] [[package]] name = "aws-types" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8140b89d76f67be2c136d7393e7e6d8edd65424eb58214839efbf4a2e4f7e8a3" +checksum = "6dd209616cc8d7bfb82f87811a5c655dc97537f592689b18743bddf5dc5c4829" dependencies = [ + "aws-credential-types", "aws-smithy-async", "aws-smithy-client", "aws-smithy-http", @@ -438,14 +497,22 @@ dependencies = [ "http", "rustc_version", "tracing", - "zeroize", ] [[package]] -name = "base64" -version = "0.12.3" +name = "backtrace" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "base64" @@ -455,9 +522,19 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] [[package]] name = "bitflags" @@ -465,20 +542,26 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" @@ -488,9 +571,12 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "bytes-utils" @@ -544,28 +630,25 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", - "js-sys", - "num-integer", "num-traits", "serde", - "time 0.1.45", - "wasm-bindgen", "winapi", ] [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -577,15 +660,15 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -597,16 +680,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "core-foundation" version = "0.9.3" @@ -619,24 +692,24 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] [[package]] name = "crc32c" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfea2db42e9927a3845fb268a10a72faed6d416065f77873f05e411457c363e" +checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" dependencies = [ "rustc_version", ] @@ -650,21 +723,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -679,69 +742,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ct-logs" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" -dependencies = [ - "sct 0.6.1", -] - -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "cxx" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "diff" version = "0.1.13" @@ -750,25 +750,26 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] name = "dyn-clone" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" @@ -779,6 +780,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -788,11 +810,17 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -821,19 +849,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -846,9 +873,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -856,15 +883,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -873,38 +900,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -920,9 +947,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -930,20 +957,28 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", + "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.15" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -981,12 +1016,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -994,6 +1026,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "0.2.9" @@ -1030,9 +1071,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -1054,32 +1095,31 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.22.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "ct-logs", - "futures-util", + "http", "hyper", "log", - "rustls 0.19.1", + "rustls 0.20.8", "rustls-native-certs", "tokio", - "tokio-rustls 0.22.0", - "webpki 0.21.4", + "tokio-rustls 0.23.4", ] [[package]] name = "hyper-rustls" -version = "0.23.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", - "rustls 0.20.8", + "rustls 0.21.5", "tokio", - "tokio-rustls 0.23.4", + "tokio-rustls 0.24.1", ] [[package]] @@ -1095,62 +1135,44 @@ dependencies = [ "tokio-native-tls", ] -[[package]] -name = "hyperx" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c" -dependencies = [ - "base64 0.13.1", - "bytes", - "http", - "httpdate", - "language-tags", - "mime", - "percent-encoding", - "unicase", -] - [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] name = "idna" -version = "0.2.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -1163,36 +1185,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonwebtoken" -version = "7.2.0" +version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.12.3", + "base64 0.21.2", "pem", "ring", "serde", @@ -1200,12 +1225,6 @@ dependencies = [ "simple_asn1", ] -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - [[package]] name = "lazy_static" version = "1.4.0" @@ -1214,24 +1233,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "linux-raw-sys" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -1239,11 +1255,10 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" dependencies = [ - "cfg-if", "serde", ] @@ -1253,14 +1268,14 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] -name = "matches" -version = "0.1.10" +name = "matchit" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" [[package]] name = "md-5" @@ -1279,9 +1294,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -1295,23 +1310,22 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "wasi", + "windows-sys", ] [[package]] @@ -1344,9 +1358,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.2.6" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", @@ -1365,37 +1379,47 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.2", "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "octorust" -version = "0.1.37" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9541376e7f3dd40c4efdf2a7342d961ce506f33cf0cb828b7ca5b7b1d83dce87" +checksum = "35a776c05d0cdd02480c12cf1484e0f3bf610eab717ba6fb2c51449b60c7a88f" dependencies = [ - "anyhow", "async-recursion", + "async-trait", + "bytes", "chrono", "http", - "hyperx", "jsonwebtoken", "log", "mime", + "parse_link_header", "pem", "percent-encoding", "reqwest", @@ -1408,23 +1432,25 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "thiserror", "tokio", "url", + "uuid", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -1435,13 +1461,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -1452,50 +1478,27 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" -dependencies = [ - "async-trait", - "crossbeam-channel", - "futures-channel", - "futures-executor", - "futures-util", - "js-sys", - "lazy_static", - "percent-encoding", - "pin-project", - "rand", - "thiserror", -] - [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] -name = "output_vt100" -version = "0.1.3" +name = "outref" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" [[package]] name = "overload" @@ -1503,6 +1506,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1510,64 +1524,87 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.8", ] [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", + "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", - "windows-sys 0.45.0", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets", +] + +[[package]] +name = "parse_link_header" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3687fe9debbbf2a019f381a8bc6b42049b22647449b39af54b3013985c0cf6de" +dependencies = [ + "http", + "lazy_static", + "regex", ] [[package]] name = "pem" -version = "0.8.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ "base64 0.13.1", - "once_cell", - "regex", ] [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -1577,9 +1614,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "ppv-lite86" @@ -1589,13 +1626,11 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -1608,7 +1643,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1625,18 +1660,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -1677,18 +1712,28 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", ] [[package]] @@ -1697,31 +1742,39 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -1730,7 +1783,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.23.2", + "hyper-rustls 0.24.1", "hyper-tls", "ipnet", "js-sys", @@ -1741,14 +1794,14 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.20.8", + "rustls 0.21.5", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls 0.23.4", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -1760,9 +1813,9 @@ dependencies = [ [[package]] name = "reqwest-conditional-middleware" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce134f515eb4c2748bbd928086e7b0aae0d1568daf6c63b51e829aa6f2cf464" +checksum = "59e50a2e70970896c99d1b8f20ddc30a70b30d3ac6e619a03a8353b64a49b277" dependencies = [ "async-trait", "reqwest", @@ -1772,13 +1825,12 @@ dependencies = [ [[package]] name = "reqwest-middleware" -version = "0.1.6" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69539cea4148dce683bec9dc95be3f0397a9bb2c248a49c8296a9d21659a8cdd" +checksum = "4531c89d50effe1fac90d095c8b133c20c5c714204feee0bfc3fd158e784209d" dependencies = [ "anyhow", "async-trait", - "futures", "http", "reqwest", "serde", @@ -1788,38 +1840,41 @@ dependencies = [ [[package]] name = "reqwest-retry" -version = "0.1.5" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce246a729eaa6aff5e215aee42845bf5fed9893cc6cd51aeeb712f34e04dd9f3" +checksum = "48d0fd6ef4c6d23790399fe15efc8d12cd9f3d4133958f9bd7801ee5cbaec6c4" dependencies = [ "anyhow", "async-trait", "chrono", "futures", + "getrandom", "http", "hyper", + "parking_lot 0.11.2", "reqwest", "reqwest-middleware", "retry-policies", "task-local-extensions", "tokio", "tracing", + "wasm-timer", ] [[package]] name = "reqwest-tracing" -version = "0.2.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c2af6c0571bef48275098bebe17863764989276c49433dab1d52328f744f1d" +checksum = "1b97ad83c2fc18113346b7158d79732242002427c30f620fa817c1f32901e0a8" dependencies = [ + "anyhow", "async-trait", - "opentelemetry", + "getrandom", + "matchit", "reqwest", "reqwest-middleware", "task-local-extensions", - "tokio", "tracing", - "tracing-opentelemetry", ] [[package]] @@ -1848,6 +1903,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1857,72 +1918,94 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustls" -version = "0.19.1" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ - "base64 0.13.1", "log", "ring", - "sct 0.6.1", - "webpki 0.21.4", + "sct", + "webpki", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", - "sct 0.7.0", - "webpki 0.22.0", + "rustls-webpki", + "sct", ] [[package]] name = "rustls-native-certs" -version = "0.5.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls 0.19.1", + "rustls-pemfile", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.2", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" dependencies = [ - "base64 0.21.0", + "ring", + "untrusted", ] [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "schemars" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "bytes", "chrono", @@ -1936,37 +2019,21 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 1.0.109", ] [[package]] name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - -[[package]] -name = "sct" -version = "0.6.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -1980,11 +2047,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1993,9 +2060,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -2003,28 +2070,28 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -2035,14 +2102,14 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -2074,9 +2141,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -2103,13 +2170,14 @@ dependencies = [ [[package]] name = "simple_asn1" -version = "0.4.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "chrono", "num-bigint", "num-traits", + "thiserror", + "time", ] [[package]] @@ -2123,9 +2191,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "smithy-rs-tool-common" @@ -2146,9 +2214,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -2166,11 +2234,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" -version = "1.0.107" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -2179,25 +2264,24 @@ dependencies = [ [[package]] name = "task-local-extensions" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4167afbec18ae012de40f8cf1b9bf48420abb390678c34821caa07d924941cc4" +checksum = "ba323866e5d033818e3240feeb9f7db2c4296674e4d9e16b97b7bf8f490434e8" dependencies = [ - "tokio", + "pin-utils", ] [[package]] name = "tempfile" -version = "3.3.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "fastrand 2.0.0", + "redox_syscall 0.3.5", + "rustix", + "windows-sys", ] [[package]] @@ -2217,22 +2301,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -2247,21 +2331,11 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ + "itoa", "serde", "time-core", "time-macros", @@ -2269,15 +2343,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.7" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" dependencies = [ "time-core", ] @@ -2299,33 +2373,33 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] @@ -2340,31 +2414,30 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.22.0" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.19.1", + "rustls 0.20.8", "tokio", - "webpki 0.21.4", + "webpki", ] [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.20.8", + "rustls 0.21.5", "tokio", - "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -2373,9 +2446,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -2438,20 +2511,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -2468,25 +2541,11 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" -dependencies = [ - "once_cell", - "opentelemetry", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", -] - [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -2523,15 +2582,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -2542,12 +2601,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "untrusted" version = "0.7.1" @@ -2556,9 +2609,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -2568,15 +2621,19 @@ dependencies = [ [[package]] name = "urlencoding" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "0.8.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom", + "serde", +] [[package]] name = "valuable" @@ -2596,22 +2653,21 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2620,9 +2676,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2630,24 +2686,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -2657,9 +2713,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2667,41 +2723,46 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] -name = "web-sys" -version = "0.3.61" +name = "wasm-timer" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ + "futures", "js-sys", + "parking_lot 0.11.2", + "pin-utils", "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "webpki" -version = "0.21.4" +name = "web-sys" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ - "ring", - "untrusted", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -2720,7 +2781,7 @@ version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "webpki 0.22.0", + "webpki", ] [[package]] @@ -2755,34 +2816,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -2795,45 +2850,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" @@ -2846,9 +2901,9 @@ dependencies = [ [[package]] name = "xmlparser" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" [[package]] name = "yansi" @@ -2858,15 +2913,15 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zip" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "byteorder", "crc32fast", diff --git a/tools/ci-cdk/canary-runner/Cargo.toml b/tools/ci-cdk/canary-runner/Cargo.toml index d2325d59f9..85e8a03be1 100644 --- a/tools/ci-cdk/canary-runner/Cargo.toml +++ b/tools/ci-cdk/canary-runner/Cargo.toml @@ -12,15 +12,15 @@ publish = false [dependencies] anyhow = "1" async-trait = "0.1.56" -aws-config = "0.47.0" -aws-sdk-cloudwatch = "0.17.0" -aws-sdk-lambda = "0.17.0" -aws-sdk-s3 = "0.17.0" +aws-config = "0.55.3" +aws-sdk-cloudwatch = "0.28.0" +aws-sdk-lambda = "0.28.0" +aws-sdk-s3 = "0.28.0" base64 = "0.13" clap = { version = "3.2.17", features = ["derive"] } hex = "0.4.3" lazy_static = "1" -octorust = "0.1.37" +octorust = "0.7.0" regex = "1.6.0" semver = "1" serde = { version = "1", features = ["derive"] } diff --git a/tools/ci-cdk/canary-runner/src/generate_matrix.rs b/tools/ci-cdk/canary-runner/src/generate_matrix.rs index 401e18a99a..bb2cb48da4 100644 --- a/tools/ci-cdk/canary-runner/src/generate_matrix.rs +++ b/tools/ci-cdk/canary-runner/src/generate_matrix.rs @@ -65,6 +65,7 @@ impl RetrieveReleases for GitHubRetrieveReleases { .repos() .list_tags(owner, repo, 100, page_num) .await? + .body .into_iter() .filter_map(|tag| ReleaseTag::from_str(&tag.name).ok()) .collect(); diff --git a/tools/ci-cdk/canary-runner/src/run.rs b/tools/ci-cdk/canary-runner/src/run.rs index 5d03cd44fd..1952323dab 100644 --- a/tools/ci-cdk/canary-runner/src/run.rs +++ b/tools/ci-cdk/canary-runner/src/run.rs @@ -21,8 +21,8 @@ use std::{env, path::Path}; use anyhow::{bail, Context, Result}; use clap::Parser; -use cloudwatch::model::StandardUnit; -use s3::types::ByteStream; +use cloudwatch::types::StandardUnit; +use s3::primitives::ByteStream; use serde::Deserialize; use smithy_rs_tool_common::git::{find_git_repository_root, Git, GitCLI}; use smithy_rs_tool_common::macros::here; @@ -83,6 +83,11 @@ pub struct RunArgs { #[clap(long)] musl: bool, + /// Expected speech text generated by Transcribe. This needs to be passed-in + /// because it can change as the accuracy of generated text improves over time. + #[clap(long)] + expected_speech_text_by_transcribe: Option, + /// File path to a CDK outputs JSON file. This can be used instead /// of all the --lambda... args. #[clap(long)] @@ -101,12 +106,13 @@ pub struct RunArgs { lambda_execution_role_arn: Option, } -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] struct Options { rust_version: Option, sdk_release_tag: Option, sdk_path: Option, musl: bool, + expected_speech_text_by_transcribe: Option, lambda_code_s3_bucket_name: String, lambda_test_s3_bucket_name: String, lambda_execution_role_arn: String, @@ -139,6 +145,7 @@ impl Options { sdk_release_tag: run_opt.sdk_release_tag, sdk_path: run_opt.sdk_path, musl: run_opt.musl, + expected_speech_text_by_transcribe: run_opt.expected_speech_text_by_transcribe, lambda_code_s3_bucket_name: value.inner.lambda_code_s3_bucket_name, lambda_test_s3_bucket_name: value.inner.lambda_test_s3_bucket_name, lambda_execution_role_arn: value.inner.lambda_execution_role_arn, @@ -149,6 +156,7 @@ impl Options { sdk_release_tag: run_opt.sdk_release_tag, sdk_path: run_opt.sdk_path, musl: run_opt.musl, + expected_speech_text_by_transcribe: run_opt.expected_speech_text_by_transcribe, lambda_code_s3_bucket_name: run_opt.lambda_code_s3_bucket_name.expect("required"), lambda_test_s3_bucket_name: run_opt.lambda_test_s3_bucket_name.expect("required"), lambda_execution_role_arn: run_opt.lambda_execution_role_arn.expect("required"), @@ -194,7 +202,7 @@ pub async fn run(opt: RunArgs) -> Result<()> { .namespace("aws-sdk-rust-canary"); for metric in metrics { request_builder = request_builder.metric_data( - cloudwatch::model::MetricDatum::builder() + cloudwatch::types::MetricDatum::builder() .metric_name(metric.0) .value(metric.1) .timestamp(SystemTime::now().into()) @@ -250,6 +258,7 @@ async fn run_canary(options: &Options, config: &aws_config::SdkConfig) -> Result bundle_name, bundle_file_name, &options.lambda_execution_role_arn, + options.expected_speech_text_by_transcribe.as_ref(), &options.lambda_code_s3_bucket_name, &options.lambda_test_s3_bucket_name, ) @@ -324,10 +333,26 @@ async fn create_lambda_fn( bundle_name: &str, bundle_file_name: &str, execution_role: &str, + expected_speech_text_by_transcribe: Option<&String>, code_s3_bucket: &str, test_s3_bucket: &str, ) -> Result<()> { - use lambda::model::*; + use lambda::types::*; + + let env_builder = match expected_speech_text_by_transcribe { + Some(expected_speech_text_by_transcribe) => Environment::builder() + .variables("RUST_BACKTRACE", "1") + .variables("RUST_LOG", "info") + .variables("CANARY_S3_BUCKET_NAME", test_s3_bucket) + .variables( + "CANARY_EXPECTED_TRANSCRIBE_RESULT", + expected_speech_text_by_transcribe, + ), + None => Environment::builder() + .variables("RUST_BACKTRACE", "1") + .variables("RUST_LOG", "info") + .variables("CANARY_S3_BUCKET_NAME", test_s3_bucket), + }; lambda_client .create_function() @@ -342,17 +367,7 @@ async fn create_lambda_fn( .build(), ) .publish(true) - .environment( - Environment::builder() - .variables("RUST_BACKTRACE", "1") - .variables("RUST_LOG", "info") - .variables("CANARY_S3_BUCKET_NAME", test_s3_bucket) - .variables( - "CANARY_EXPECTED_TRANSCRIBE_RESULT", - "Good day to you transcribe. This is Polly talking to you from the Rust ST K.", - ) - .build(), - ) + .environment(env_builder.build()) .timeout(60) .send() .await @@ -379,8 +394,8 @@ async fn create_lambda_fn( } async fn invoke_lambda(lambda_client: lambda::Client, bundle_name: &str) -> Result<()> { - use lambda::model::*; - use lambda::types::Blob; + use lambda::primitives::Blob; + use lambda::types::*; let response = lambda_client .invoke() @@ -419,3 +434,68 @@ async fn delete_lambda_fn(lambda_client: lambda::Client, bundle_name: &str) -> R .context(here!("failed to delete Lambda"))?; Ok(()) } + +#[cfg(test)] +mod tests { + use crate::run::Options; + use crate::run::RunArgs; + use clap::Parser; + + #[test] + fn args_parsing() { + assert_eq!( + RunArgs { + rust_version: None, + sdk_release_tag: None, + sdk_path: Some("artifact-aws-sdk-rust/sdk".into()), + musl: false, + expected_speech_text_by_transcribe: Some("Good day to you transcribe.".to_owned()), + cdk_output: Some("../cdk-outputs.json".into()), + lambda_code_s3_bucket_name: None, + lambda_test_s3_bucket_name: None, + lambda_execution_role_arn: None + }, + RunArgs::try_parse_from([ + "run", + "--sdk-path", + "artifact-aws-sdk-rust/sdk", + "--expected-speech-text-by-transcribe", + "Good day to you transcribe.", + "--cdk-output", + "../cdk-outputs.json", + ]) + .unwrap() + ); + } + + #[test] + fn options_from_args() { + let run_args = RunArgs::try_parse_from([ + "run", + "--sdk-path", + "artifact-aws-sdk-rust/sdk", + "--expected-speech-text-by-transcribe", + "Good day to you transcribe.", + "--lambda-code-s3-bucket-name", + "bucket-for-code", + "--lambda-test-s3-bucket-name", + "bucket-for-test", + "--lambda-execution-role-arn", + "arn:aws:lambda::role/exe-role", + ]) + .unwrap(); + assert_eq!( + Options { + rust_version: None, + sdk_release_tag: None, + sdk_path: Some("artifact-aws-sdk-rust/sdk".into()), + musl: false, + expected_speech_text_by_transcribe: Some("Good day to you transcribe.".to_owned()), + lambda_code_s3_bucket_name: "bucket-for-code".to_owned(), + lambda_test_s3_bucket_name: "bucket-for-test".to_owned(), + lambda_execution_role_arn: "arn:aws:lambda::role/exe-role".to_owned(), + }, + Options::load_from(run_args).unwrap(), + ); + } +} diff --git a/tools/ci-cdk/lib/oidc-provider-stack.ts b/tools/ci-cdk/lib/oidc-provider-stack.ts index abcf6c5cea..45a0d40cee 100644 --- a/tools/ci-cdk/lib/oidc-provider-stack.ts +++ b/tools/ci-cdk/lib/oidc-provider-stack.ts @@ -17,7 +17,13 @@ import { Construct } from "constructs"; /// /// This was done with the initial Idp URL of: /// https://token.actions.githubusercontent.com/.well-known/openid-configuration -export const GITHUB_CERTIFICATE_THUMBPRINT = "6938FD4D98BAB03FAADB97B34396831E3780AEA1"; +/// +/// Note: as of June 27, 2023, there are now two possible thumbprints from GitHub: +/// https://github.blog/changelog/2023-06-27-github-actions-update-on-oidc-integration-with-aws/ +export const GITHUB_CERTIFICATE_THUMBPRINTS = [ + "6938FD4D98BAB03FAADB97B34396831E3780AEA1", + "1C58A3A8518E8759BF075B76B750D4F2DF264FCD", +]; // There can only be one OIDC provider for a given URL per AWS account, // so put these in their own stack to be shared with other stacks. @@ -32,7 +38,7 @@ export class OidcProviderStack extends Stack { this.githubActionsOidcProvider = new OpenIdConnectProvider(this, "oidc-provider", { url: "https://token.actions.githubusercontent.com", - thumbprints: [GITHUB_CERTIFICATE_THUMBPRINT], + thumbprints: GITHUB_CERTIFICATE_THUMBPRINTS, clientIds: ["sts.amazonaws.com"], }); } diff --git a/tools/ci-cdk/package-lock.json b/tools/ci-cdk/package-lock.json index f42ae7c338..b16f3ff4df 100644 --- a/tools/ci-cdk/package-lock.json +++ b/tools/ci-cdk/package-lock.json @@ -29,12 +29,12 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -42,64 +42,64 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.37", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.37.tgz", - "integrity": "sha512-1jLfQLpFEurbcBuwBLgw7u7OxRYQZTTl9L8/6Bx44fQksJch+9dyvbVnbhigAfc9K9z2e0QAsepZybE6lAGCDQ==", + "version": "2.2.199", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.199.tgz", + "integrity": "sha512-zNdD2OxALdsdQaRZBpTfMTuudxV+4jLMznJIvVj6O+OqCru4m5UtgVQmyApW1z2H9s4/06ovVt20aXe2G8Ta+w==", "dev": true }, "node_modules/@aws-cdk/asset-kubectl-v20": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", - "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==", + "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==", "dev": true }, "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.38.tgz", - "integrity": "sha512-BBwAjORhuUkTGO3CxGS5Evcp5n20h9v06Sftn2R1DuSm8zIoUlPsNlI1HUk8XqYuoEI4aD7IKRQBLglv09ciJQ==", + "version": "2.0.165", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.165.tgz", + "integrity": "sha512-bsyLQD/vqXQcc9RDmlM1XqiFNO/yewgVFXmkMcQkndJbmE/jgYkzewwYGrBlfL725hGLQipXq19+jwWwdsXQqg==", "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", + "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.7.tgz", - "integrity": "sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.7", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", + "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" }, "engines": { @@ -120,41 +120,28 @@ } }, "node_modules/@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", "dev": true, "dependencies": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", + "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@babel/compat-data": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.0" @@ -176,151 +163,151 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz", - "integrity": "sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", + "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", + "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -400,9 +387,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", + "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -559,12 +546,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -574,33 +561,33 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.20.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.8.tgz", - "integrity": "sha512-/RNkaYDeCy4MjyV70+QkSHhxbvj2JO/5Ft2Pa880qJOG8tWrqcT/wXUuCCv43yogfqPzHL77Xu101KQPf4clnQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", + "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -618,13 +605,13 @@ } }, "node_modules/@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -659,15 +646,39 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", - "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -682,10 +693,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/js": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -1064,13 +1084,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -1095,21 +1116,27 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1191,19 +1218,19 @@ "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "node_modules/@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" @@ -1229,18 +1256,18 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", "dev": true, "dependencies": { "@types/node": "*" @@ -1281,9 +1308,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "node_modules/@types/node": { @@ -1293,15 +1320,15 @@ "dev": true }, "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1311,9 +1338,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1326,18 +1353,19 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz", - "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", + "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/type-utils": "5.47.0", - "@typescript-eslint/utils": "5.47.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/type-utils": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" }, @@ -1359,14 +1387,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.0.tgz", - "integrity": "sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", + "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "debug": "^4.3.4" }, "engines": { @@ -1386,13 +1414,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz", - "integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", + "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/visitor-keys": "5.47.0" + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1403,13 +1431,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz", - "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", + "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.47.0", - "@typescript-eslint/utils": "5.47.0", + "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1430,9 +1458,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz", - "integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1443,13 +1471,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz", - "integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", + "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/visitor-keys": "5.47.0", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1470,18 +1498,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz", - "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", + "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", "dev": true, "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -1496,12 +1524,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz", - "integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.47.0", + "@typescript-eslint/types": "5.60.1", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1519,9 +1547,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1690,9 +1718,9 @@ "dev": true }, "node_modules/aws-cdk": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.56.0.tgz", - "integrity": "sha512-UjzCnuW5uw10MrzlOqp/cc8dGfTw5Og9YpMWBlCrYA+kVSSS2ikc6aWnk0IM07RQLr9RTvAzXkT2IfVivY3baQ==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.85.0.tgz", + "integrity": "sha512-duRE5rvP9Qu5iUNgA6+knHKsQ7xI6yKMUxyARTveYEzW/qDHD0RWKRu+pDbbwXLlzcr25oKGPjC3dM0ui2beKg==", "dev": true, "bin": { "cdk": "bin/cdk" @@ -1705,9 +1733,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.56.0.tgz", - "integrity": "sha512-WFcqPzqiVsRCDGWq+rW2RRKsqg6ijCsDGwPWZmRyTkWurHEFk6BUbU4peRaUw5xeSP/qH9WcL17Tt7CF5Z1UVg==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.85.0.tgz", + "integrity": "sha512-u+ypK8XEMRH3tGRMSmcbPYxLet7xBdGIztUkMcPtlNJGhS/vxqh12yYkem3g3zzmHwdX8OPLSnlZ2sIuiIqp/g==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -1717,21 +1745,23 @@ "minimatch", "punycode", "semver", + "table", "yaml" ], "dev": true, "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.30", + "@aws-cdk/asset-awscli-v1": "^2.2.177", "@aws-cdk/asset-kubectl-v20": "^2.1.1", - "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.38", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^9.1.0", - "ignore": "^5.2.1", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", "jsonschema": "^1.4.1", "minimatch": "^3.1.2", - "punycode": "^2.1.1", - "semver": "^7.3.8", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", "yaml": "1.10.2" }, "engines": { @@ -1747,13 +1777,53 @@ "inBundle": true, "license": "Apache-2.0" }, - "node_modules/aws-cdk-lib/node_modules/at-least-node": { - "version": "1.0.0", + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.12.0", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/aws-cdk-lib/node_modules/balanced-match": { @@ -1781,35 +1851,64 @@ "node": ">= 0.8.0" } }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/concat-map": { "version": "0.0.1", "dev": true, "inBundle": true, "license": "MIT" }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "9.1.0", + "version": "11.1.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=14.14" } }, "node_modules/aws-cdk-lib/node_modules/graceful-fs": { - "version": "4.2.10", + "version": "4.2.11", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.2.1", + "version": "5.2.4", "dev": true, "inBundle": true, "license": "MIT", @@ -1817,6 +1916,21 @@ "node": ">= 4" } }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/jsonfile": { "version": "6.1.0", "dev": true, @@ -1838,6 +1952,12 @@ "node": "*" } }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/lru-cache": { "version": "6.0.0", "dev": true, @@ -1863,7 +1983,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/punycode": { - "version": "2.1.1", + "version": "2.3.0", "dev": true, "inBundle": true, "license": "MIT", @@ -1871,8 +1991,17 @@ "node": ">=6" } }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.3.8", + "version": "7.5.2", "dev": true, "inBundle": true, "license": "ISC", @@ -1886,6 +2015,65 @@ "node": ">=10" } }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.8.1", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/aws-cdk-lib/node_modules/universalify": { "version": "2.0.0", "dev": true, @@ -1895,6 +2083,15 @@ "node": ">= 10.0.0" } }, + "node_modules/aws-cdk-lib/node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/aws-cdk-lib/node_modules/yallist": { "version": "4.0.0", "dev": true, @@ -2037,9 +2234,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -2049,13 +2246,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -2110,9 +2311,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001441", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz", - "integrity": "sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg==", + "version": "1.0.30001508", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", + "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", "dev": true, "funding": [ { @@ -2122,6 +2323,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -2151,18 +2356,24 @@ } }, "node_modules/ci-info": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", - "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { "node": ">=8" } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "node_modules/cliui": { @@ -2229,12 +2440,12 @@ "dev": true }, "node_modules/constructs": { - "version": "10.1.197", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.197.tgz", - "integrity": "sha512-YAuSMYDXPQvNqmCrnVBGHXGL1+8WICDDJLH91BUIWfL6PpMEm+LXBXchgGETyREf96nIIGuXC/0lkUURsLSrHA==", + "version": "10.2.61", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.61.tgz", + "integrity": "sha512-8l099E1XPLN6VgUitXP6wPHLl25vc8RgxePhbCY/iRF9RGErSy6JHnI3AJU8XJ3KMaUZqhq+q9q3o+KoKM1eVw==", "dev": true, "engines": { - "node": ">= 14.17.0" + "node": ">= 16.14.0" } }, "node_modules/convert-source-map": { @@ -2337,9 +2548,9 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" @@ -2427,9 +2638,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.442", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.442.tgz", + "integrity": "sha512-RkrZF//Ya+0aJq2NM3OdisNh5ZodZq1rdXOS96G8DdDgpDKqKE81yTbbQ3F/4CKm1JBPsGu1Lp/akkna2xO06Q==", "dev": true }, "node_modules/emittery": { @@ -2563,13 +2774,16 @@ } }, "node_modules/eslint": { - "version": "8.30.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", - "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.43.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -2578,24 +2792,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -2603,7 +2815,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -2619,9 +2830,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -2643,46 +2854,22 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2690,6 +2877,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -2702,14 +2892,14 @@ } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2732,9 +2922,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -2885,9 +3075,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -3073,9 +3263,9 @@ } }, "node_modules/globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3108,9 +3298,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/grapheme-splitter": { @@ -3119,6 +3309,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3282,9 +3478,9 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4068,16 +4264,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4173,9 +4359,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -4397,9 +4583,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "node_modules/normalize-path": { @@ -4424,9 +4610,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", + "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", "dev": true }, "node_modules/once": { @@ -4606,9 +4792,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -4688,9 +4874,9 @@ } }, "node_modules/prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -4748,9 +4934,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" @@ -4788,18 +4974,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4816,12 +4990,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4863,9 +5037,9 @@ } }, "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", "dev": true, "engines": { "node": ">=10" @@ -4938,9 +5112,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5207,9 +5381,9 @@ "dev": true }, "node_modules/throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "dev": true }, "node_modules/tmpl": { @@ -5240,9 +5414,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { "psl": "^1.1.33", @@ -5447,9 +5621,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -5459,6 +5633,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -5466,7 +5644,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -5747,68 +5925,68 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "requires": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "@aws-cdk/asset-awscli-v1": { - "version": "2.2.37", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.37.tgz", - "integrity": "sha512-1jLfQLpFEurbcBuwBLgw7u7OxRYQZTTl9L8/6Bx44fQksJch+9dyvbVnbhigAfc9K9z2e0QAsepZybE6lAGCDQ==", + "version": "2.2.199", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.199.tgz", + "integrity": "sha512-zNdD2OxALdsdQaRZBpTfMTuudxV+4jLMznJIvVj6O+OqCru4m5UtgVQmyApW1z2H9s4/06ovVt20aXe2G8Ta+w==", "dev": true }, "@aws-cdk/asset-kubectl-v20": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", - "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==", + "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==", "dev": true }, "@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.38.tgz", - "integrity": "sha512-BBwAjORhuUkTGO3CxGS5Evcp5n20h9v06Sftn2R1DuSm8zIoUlPsNlI1HUk8XqYuoEI4aD7IKRQBLglv09ciJQ==", + "version": "2.0.165", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.165.tgz", + "integrity": "sha512-bsyLQD/vqXQcc9RDmlM1XqiFNO/yewgVFXmkMcQkndJbmE/jgYkzewwYGrBlfL725hGLQipXq19+jwWwdsXQqg==", "dev": true }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" } }, "@babel/compat-data": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", + "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", "dev": true }, "@babel/core": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.7.tgz", - "integrity": "sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.7", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", + "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" }, "dependencies": { @@ -5821,37 +5999,25 @@ } }, "@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", "dev": true, "requires": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } } }, "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", + "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", "dev": true, "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@babel/compat-data": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.0" @@ -5866,115 +6032,115 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-transforms": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz", - "integrity": "sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true }, "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "requires": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", + "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true }, "@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", + "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", "dev": true, "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -6038,9 +6204,9 @@ } }, "@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", + "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -6152,39 +6318,39 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/traverse": { - "version": "7.20.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.8.tgz", - "integrity": "sha512-/RNkaYDeCy4MjyV70+QkSHhxbvj2JO/5Ft2Pa880qJOG8tWrqcT/wXUuCCv43yogfqPzHL77Xu101KQPf4clnQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", + "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -6198,13 +6364,13 @@ } }, "@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" } }, @@ -6235,15 +6401,30 @@ } } }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", - "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -6252,10 +6433,16 @@ "strip-json-comments": "^3.1.1" } }, + "@eslint/js": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "dev": true + }, "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -6550,13 +6737,14 @@ } }, "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@jridgewell/resolve-uri": { @@ -6572,19 +6760,27 @@ "dev": true }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "requires": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + } } }, "@nodelib/fs.scandir": { @@ -6656,19 +6852,19 @@ "dev": true }, "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dev": true, "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" @@ -6694,18 +6890,18 @@ } }, "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dev": true, "requires": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", "dev": true, "requires": { "@types/node": "*" @@ -6746,9 +6942,9 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "@types/node": { @@ -6758,15 +6954,15 @@ "dev": true }, "@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "@types/stack-utils": { @@ -6776,9 +6972,9 @@ "dev": true }, "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -6791,70 +6987,71 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz", - "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", + "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/type-utils": "5.47.0", - "@typescript-eslint/utils": "5.47.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/type-utils": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" } }, "@typescript-eslint/parser": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.0.tgz", - "integrity": "sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", + "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz", - "integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", + "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/visitor-keys": "5.47.0" + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1" } }, "@typescript-eslint/type-utils": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz", - "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", + "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.47.0", - "@typescript-eslint/utils": "5.47.0", + "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz", - "integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz", - "integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", + "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/visitor-keys": "5.47.0", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -6863,28 +7060,28 @@ } }, "@typescript-eslint/utils": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz", - "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", + "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", "dev": true, "requires": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz", - "integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.47.0", + "@typescript-eslint/types": "5.60.1", "eslint-visitor-keys": "^3.3.0" } }, @@ -6895,9 +7092,9 @@ "dev": true }, "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true }, "acorn-globals": { @@ -7019,31 +7216,32 @@ "dev": true }, "aws-cdk": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.56.0.tgz", - "integrity": "sha512-UjzCnuW5uw10MrzlOqp/cc8dGfTw5Og9YpMWBlCrYA+kVSSS2ikc6aWnk0IM07RQLr9RTvAzXkT2IfVivY3baQ==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.85.0.tgz", + "integrity": "sha512-duRE5rvP9Qu5iUNgA6+knHKsQ7xI6yKMUxyARTveYEzW/qDHD0RWKRu+pDbbwXLlzcr25oKGPjC3dM0ui2beKg==", "dev": true, "requires": { "fsevents": "2.3.2" } }, "aws-cdk-lib": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.56.0.tgz", - "integrity": "sha512-WFcqPzqiVsRCDGWq+rW2RRKsqg6ijCsDGwPWZmRyTkWurHEFk6BUbU4peRaUw5xeSP/qH9WcL17Tt7CF5Z1UVg==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.85.0.tgz", + "integrity": "sha512-u+ypK8XEMRH3tGRMSmcbPYxLet7xBdGIztUkMcPtlNJGhS/vxqh12yYkem3g3zzmHwdX8OPLSnlZ2sIuiIqp/g==", "dev": true, "requires": { - "@aws-cdk/asset-awscli-v1": "^2.2.30", + "@aws-cdk/asset-awscli-v1": "^2.2.177", "@aws-cdk/asset-kubectl-v20": "^2.1.1", - "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.38", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^9.1.0", - "ignore": "^5.2.1", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", "jsonschema": "^1.4.1", "minimatch": "^3.1.2", - "punycode": "^2.1.1", - "semver": "^7.3.8", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", "yaml": "1.10.2" }, "dependencies": { @@ -7052,8 +7250,32 @@ "bundled": true, "dev": true }, - "at-least-node": { - "version": "1.0.0", + "ajv": { + "version": "8.12.0", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", "bundled": true, "dev": true }, @@ -7076,29 +7298,61 @@ "bundled": true, "dev": true }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, "concat-map": { "version": "0.0.1", "bundled": true, "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, "fs-extra": { - "version": "9.1.0", + "version": "11.1.1", "bundled": true, "dev": true, "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "graceful-fs": { - "version": "4.2.10", + "version": "4.2.11", "bundled": true, "dev": true }, "ignore": { - "version": "5.2.1", + "version": "5.2.4", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", "bundled": true, "dev": true }, @@ -7116,6 +7370,11 @@ "bundled": true, "dev": true }, + "lodash.truncate": { + "version": "4.4.2", + "bundled": true, + "dev": true + }, "lru-cache": { "version": "6.0.0", "bundled": true, @@ -7133,23 +7392,76 @@ } }, "punycode": { - "version": "2.1.1", + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "require-from-string": { + "version": "2.0.2", "bundled": true, "dev": true }, "semver": { - "version": "7.3.8", + "version": "7.5.2", "bundled": true, "dev": true, "requires": { "lru-cache": "^6.0.0" } }, + "slice-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "table": { + "version": "6.8.1", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + } + }, "universalify": { "version": "2.0.0", "bundled": true, "dev": true }, + "uri-js": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "yallist": { "version": "4.0.0", "bundled": true, @@ -7265,15 +7577,15 @@ "dev": true }, "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" } }, "bs-logger": { @@ -7313,9 +7625,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001441", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz", - "integrity": "sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg==", + "version": "1.0.30001508", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", + "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", "dev": true }, "chalk": { @@ -7335,15 +7647,15 @@ "dev": true }, "ci-info": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", - "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true }, "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "cliui": { @@ -7400,9 +7712,9 @@ "dev": true }, "constructs": { - "version": "10.1.197", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.197.tgz", - "integrity": "sha512-YAuSMYDXPQvNqmCrnVBGHXGL1+8WICDDJLH91BUIWfL6PpMEm+LXBXchgGETyREf96nIIGuXC/0lkUURsLSrHA==", + "version": "10.2.61", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.61.tgz", + "integrity": "sha512-8l099E1XPLN6VgUitXP6wPHLl25vc8RgxePhbCY/iRF9RGErSy6JHnI3AJU8XJ3KMaUZqhq+q9q3o+KoKM1eVw==", "dev": true }, "convert-source-map": { @@ -7490,9 +7802,9 @@ "dev": true }, "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, "delayed-stream": { @@ -7555,9 +7867,9 @@ } }, "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.442", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.442.tgz", + "integrity": "sha512-RkrZF//Ya+0aJq2NM3OdisNh5ZodZq1rdXOS96G8DdDgpDKqKE81yTbbQ3F/4CKm1JBPsGu1Lp/akkna2xO06Q==", "dev": true }, "emittery": { @@ -7654,13 +7966,16 @@ } }, "eslint": { - "version": "8.30.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", - "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.4.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.43.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -7669,24 +7984,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -7694,16 +8007,15 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -7719,9 +8031,9 @@ } }, "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "requires": {} }, @@ -7735,38 +8047,21 @@ "estraverse": "^4.1.1" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true }, "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esprima": { @@ -7776,9 +8071,9 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -7899,9 +8194,9 @@ "dev": true }, "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -8038,9 +8333,9 @@ } }, "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -8061,9 +8356,9 @@ } }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "grapheme-splitter": { @@ -8072,6 +8367,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -8193,9 +8494,9 @@ "dev": true }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "requires": { "has": "^1.0.3" @@ -8802,12 +9103,6 @@ } } }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8883,9 +9178,9 @@ "dev": true }, "json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "kleur": { @@ -9061,9 +9356,9 @@ "dev": true }, "node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "normalize-path": { @@ -9082,9 +9377,9 @@ } }, "nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", + "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", "dev": true }, "once": { @@ -9213,9 +9508,9 @@ "dev": true }, "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true }, "pkg-dir": { @@ -9273,9 +9568,9 @@ "dev": true }, "prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true }, "pretty-format": { @@ -9314,9 +9609,9 @@ "dev": true }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, "querystringify": { @@ -9337,12 +9632,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9356,12 +9645,12 @@ "dev": true }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -9390,9 +9679,9 @@ "dev": true }, "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", "dev": true }, "reusify": { @@ -9435,9 +9724,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -9639,9 +9928,9 @@ "dev": true }, "throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "dev": true }, "tmpl": { @@ -9666,9 +9955,9 @@ } }, "tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "requires": { "psl": "^1.1.33", @@ -9789,9 +10078,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "requires": { "escalade": "^3.1.1", diff --git a/tools/ci-cdk/test/oidc-provider-stack.test.ts b/tools/ci-cdk/test/oidc-provider-stack.test.ts index 24a31505ba..f776fd843e 100644 --- a/tools/ci-cdk/test/oidc-provider-stack.test.ts +++ b/tools/ci-cdk/test/oidc-provider-stack.test.ts @@ -5,7 +5,7 @@ import { App } from "aws-cdk-lib"; import { Template } from "aws-cdk-lib/assertions"; -import { GITHUB_CERTIFICATE_THUMBPRINT, OidcProviderStack } from "../lib/oidc-provider-stack"; +import { GITHUB_CERTIFICATE_THUMBPRINTS, OidcProviderStack } from "../lib/oidc-provider-stack"; test("it should have an OIDC provider", () => { const app = new App(); @@ -15,7 +15,7 @@ test("it should have an OIDC provider", () => { // Verify the OIDC provider template.hasResourceProperties("Custom::AWSCDKOpenIdConnectProvider", { ClientIDList: ["sts.amazonaws.com"], - ThumbprintList: [GITHUB_CERTIFICATE_THUMBPRINT], + ThumbprintList: GITHUB_CERTIFICATE_THUMBPRINTS, Url: "https://token.actions.githubusercontent.com", }); }); diff --git a/tools/ci-scripts/check-aws-config b/tools/ci-scripts/check-aws-config index 60af5d28d2..5c6d910fe2 100755 --- a/tools/ci-scripts/check-aws-config +++ b/tools/ci-scripts/check-aws-config @@ -36,7 +36,7 @@ echo "${C_YELLOW}## Checking for duplicate dependency versions in the normal dep cargo tree -d --edges normal --all-features echo "${C_YELLOW}## Testing every combination of features${C_RESET}" -cargo hack test --feature-powerset --exclude-all-features +cargo hack test --feature-powerset --exclude-all-features --exclude-features native-tls echo "${C_YELLOW}## Checking the wasm32-unknown-unknown and wasm32-wasi targets${C_RESET}" cargo check --target wasm32-unknown-unknown --no-default-features diff --git a/tools/ci-scripts/check-aws-sdk-adhoc-tests b/tools/ci-scripts/check-aws-sdk-adhoc-tests index 0ab0ba71c2..911d11f998 100755 --- a/tools/ci-scripts/check-aws-sdk-adhoc-tests +++ b/tools/ci-scripts/check-aws-sdk-adhoc-tests @@ -4,6 +4,17 @@ # SPDX-License-Identifier: Apache-2.0 # -set -eux +C_YELLOW='\033[1;33m' +C_RESET='\033[0m' + +set -eu cd smithy-rs -./gradlew aws:sdk-adhoc-test:test + +# TODO(enableNewSmithyRuntimeCleanup): Remove the middleware test run when cleaning up middleware +echo -e "## ${C_YELLOW}Running SDK adhoc tests against the middleware implementation...${C_RESET}" +./gradlew aws:sdk-adhoc-test:clean +./gradlew aws:sdk-adhoc-test:check -Psmithy.runtime.mode=middleware + +echo -e "## ${C_YELLOW}Running SDK adhoc tests against the orchestrator implementation...${C_RESET}" +./gradlew aws:sdk-adhoc-test:clean +./gradlew aws:sdk-adhoc-test:check -Psmithy.runtime.mode=orchestrator diff --git a/tools/ci-scripts/check-aws-sdk-examples b/tools/ci-scripts/check-aws-sdk-examples index 46495665a0..db5880b499 100755 --- a/tools/ci-scripts/check-aws-sdk-examples +++ b/tools/ci-scripts/check-aws-sdk-examples @@ -13,6 +13,6 @@ cd aws-sdk/examples for example in *; do echo -e "${C_YELLOW}Checking examples/${example}...${C_RESET}" pushd "${example}" &>/dev/null - cargo check + cargo check && cargo clean popd &>/dev/null done diff --git a/tools/ci-scripts/check-aws-sdk-middleware-impl b/tools/ci-scripts/check-aws-sdk-middleware-impl new file mode 100755 index 0000000000..ea6cfe85df --- /dev/null +++ b/tools/ci-scripts/check-aws-sdk-middleware-impl @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# This script tests the SDK smoke test services against the middleware implementation + +C_YELLOW='\033[1;33m' +C_RESET='\033[0m' + +set -eu +cd smithy-rs + +./gradlew aws:sdk:assemble -Psmithy.runtime.mode=middleware + +cd aws/sdk/build/aws-sdk/sdk +for service in */; do + pushd "${service}" + echo -e "${C_YELLOW}# Running 'cargo test --all-features' on '${service}'${C_RESET}" + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_middleware_mode" cargo test --all-features --all-targets --no-fail-fast + echo -e "${C_YELLOW}# Running 'cargo clippy --all-features' on '${service}'${C_RESET}" + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_middleware_mode" cargo clippy --all-features + popd +done + +echo "SUCCESS" diff --git a/tools/ci-scripts/check-aws-sdk-services b/tools/ci-scripts/check-aws-sdk-services index a440c98f83..2c86e975e8 100755 --- a/tools/ci-scripts/check-aws-sdk-services +++ b/tools/ci-scripts/check-aws-sdk-services @@ -7,9 +7,8 @@ set -eux cd aws-sdk -# Remove examples from workspace -sed -i '/"examples\//d' Cargo.toml - +# Invoking `cargo test` at the root directory implicitly checks for the validity +# of the top-level `Cargo.toml` cargo test --all-features for test_dir in tests/*; do diff --git a/tools/ci-scripts/check-aws-sdk-smoketest-unit-tests b/tools/ci-scripts/check-aws-sdk-smoketest-unit-tests index 293af9b43f..bbf13d54b3 100755 --- a/tools/ci-scripts/check-aws-sdk-smoketest-unit-tests +++ b/tools/ci-scripts/check-aws-sdk-smoketest-unit-tests @@ -7,3 +7,10 @@ set -eux cd aws-sdk-smoketest cargo test --all-features + +for test_dir in tests/*; do + if [ -f "${test_dir}/Cargo.toml" ]; then + echo "#### Testing ${test_dir}..." + cargo test --all-features --manifest-path "${test_dir}/Cargo.toml" + fi +done diff --git a/tools/ci-scripts/check-book b/tools/ci-scripts/check-book new file mode 100755 index 0000000000..e26aae552b --- /dev/null +++ b/tools/ci-scripts/check-book @@ -0,0 +1,14 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +set -eux + +cd smithy-rs/examples +make +cargo b + +cd .. +mdbook test design -L target/debug/deps diff --git a/tools/ci-scripts/check-client-codegen-integration-tests b/tools/ci-scripts/check-client-codegen-integration-tests index 09ab8f735c..86013436c5 100755 --- a/tools/ci-scripts/check-client-codegen-integration-tests +++ b/tools/ci-scripts/check-client-codegen-integration-tests @@ -6,4 +6,7 @@ set -eux cd smithy-rs -./gradlew codegen-client-test:test + +# TODO(enableNewSmithyRuntimeCleanup): Only run the orchestrator version of this +./gradlew codegen-client-test:test -Psmithy.runtime.mode=middleware +./gradlew codegen-client-test:test -Psmithy.runtime.mode=orchestrator diff --git a/tools/ci-scripts/check-semver b/tools/ci-scripts/check-semver new file mode 100755 index 0000000000..a23cc3aaa9 --- /dev/null +++ b/tools/ci-scripts/check-semver @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +set -eux +cd smithy-rs + +if [[ $# -gt 2 ]]; then + echo "Usage: check-semver " + exit 1 +fi + +# Override version commit hash to prevent unnecessary diffs +export SMITHY_RS_VERSION_COMMIT_HASH_OVERRIDE=ci +base_revision="$1" +warn_on_failure="$2" +if [[ $warn_on_failure == "true" ]] +then + ./tools/ci-scripts/codegen-diff/semver-checks.py . "${base_revision}" || echo "allowing failure" +else + ./tools/ci-scripts/codegen-diff/semver-checks.py . "${base_revision}" +fi diff --git a/tools/ci-scripts/check-server-python-e2e-test b/tools/ci-scripts/check-server-python-e2e-test index fbaf9b0d67..26b9e15914 100755 --- a/tools/ci-scripts/check-server-python-e2e-test +++ b/tools/ci-scripts/check-server-python-e2e-test @@ -4,6 +4,6 @@ # SPDX-License-Identifier: Apache-2.0 set -eux -cd smithy-rs/rust-runtime/aws-smithy-http-server-python/examples +cd smithy-rs/examples/python make test clippy diff --git a/tools/ci-scripts/codegen-diff/check-deterministic-codegen.py b/tools/ci-scripts/codegen-diff/check-deterministic-codegen.py index 71e0b61541..7dc72258b2 100755 --- a/tools/ci-scripts/codegen-diff/check-deterministic-codegen.py +++ b/tools/ci-scripts/codegen-diff/check-deterministic-codegen.py @@ -3,19 +3,18 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -import sys import os -from diff_lib import get_cmd_output, generate_and_commit_generated_code +import sys + +from diff_lib import get_cmd_output, checkout_commit_and_generate + def main(): repository_root = sys.argv[1] os.chdir(repository_root) (_, head_commit_sha, _) = get_cmd_output("git rev-parse HEAD") - get_cmd_output("git checkout -B once") - generate_and_commit_generated_code(head_commit_sha, targets=['aws:sdk']) - get_cmd_output(f"git checkout {head_commit_sha}") - get_cmd_output("git checkout -B twice") - generate_and_commit_generated_code(head_commit_sha, targets=['aws:sdk']) + checkout_commit_and_generate(head_commit_sha, targets=['aws:sdk'], branch_name='once') + checkout_commit_and_generate(head_commit_sha, targets=['aws:sdk'], branch_name='twice') get_cmd_output('git diff once..twice --exit-code') diff --git a/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py b/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py index 732d223e1e..75b3b1d40a 100755 --- a/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py +++ b/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py @@ -7,7 +7,7 @@ import sys from diff_lib import eprint, run, get_cmd_status, get_cmd_output, generate_and_commit_generated_code, make_diffs, \ - write_to_file, HEAD_BRANCH_NAME, BASE_BRANCH_NAME, OUTPUT_PATH + write_to_file, HEAD_BRANCH_NAME, BASE_BRANCH_NAME, OUTPUT_PATH, running_in_docker_build # This script can be run and tested locally. To do so, you should check out @@ -20,7 +20,7 @@ # # ``` # $ cd test/smithy-rs -# $ ../../smithy-rs/tools/ci-scripts/codegen-diff-revisions.py . +# $ ../../smithy-rs/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py . # ``` # # It will diff the generated code from HEAD against any commit hash you feed it. If you want to test @@ -32,8 +32,6 @@ # ``` # Make sure the local version matches the version referenced from the GitHub Actions workflow. -def running_in_docker_build(): - return os.environ.get("SMITHY_RS_DOCKER_BUILD_IMAGE") == "1" def main(): diff --git a/tools/ci-scripts/codegen-diff/diff_lib.py b/tools/ci-scripts/codegen-diff/diff_lib.py index f48cd53ae1..d39bbaba17 100644 --- a/tools/ci-scripts/codegen-diff/diff_lib.py +++ b/tools/ci-scripts/codegen-diff/diff_lib.py @@ -2,9 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 import os -import sys -import subprocess import shlex +import subprocess +import sys HEAD_BRANCH_NAME = "__tmp-localonly-head" BASE_BRANCH_NAME = "__tmp-localonly-base" @@ -15,31 +15,55 @@ CDN_URL = "https://d2luzm2xt3nokh.cloudfront.net" +target_codegen_client = 'codegen-client-test' +target_codegen_server = 'codegen-server-test' +target_codegen_server_python = 'codegen-server-test:python' +target_codegen_server_typescript = 'codegen-server-test:typescript' +target_aws_sdk = 'aws:sdk' + + +def running_in_docker_build(): + return os.environ.get("SMITHY_RS_DOCKER_BUILD_IMAGE") == "1" + + +def checkout_commit_and_generate(revision_sha, branch_name, targets=None): + if running_in_docker_build(): + eprint(f"Fetching base revision {revision_sha} from GitHub...") + run(f"git fetch --no-tags --progress --no-recurse-submodules --depth=1 origin {revision_sha}") + + # Generate code for HEAD + eprint(f"Creating temporary branch {branch_name} with generated code for {revision_sha}") + run(f"git checkout {revision_sha} -B {branch_name}") + generate_and_commit_generated_code(revision_sha, targets) + def generate_and_commit_generated_code(revision_sha, targets=None): - targets = targets or ['codegen-client-test', 'codegen-server-test', 'aws:sdk'] + targets = targets or [ + target_codegen_client, + target_codegen_server, + target_aws_sdk, + target_codegen_server_python, + target_codegen_server_typescript + ] # Clean the build artifacts before continuing + assemble_tasks = ' '.join([f'{t}:assemble' for t in targets]) + clean_tasks = ' '.join([f'{t}:clean' for t in targets]) get_cmd_output("rm -rf aws/sdk/build") - if 'codegen-server-test' in targets: - get_cmd_output("cd rust-runtime/aws-smithy-http-server-python/examples && make distclean", shell=True) - get_cmd_output("./gradlew codegen-core:clean codegen-client:clean codegen-server:clean aws:sdk-codegen:clean") - - # Generate code - tasks = ' '.join([f'{t}:assemble' for t in targets]) - get_cmd_output(f"./gradlew --rerun-tasks {tasks}") - if 'codegen-server-test' in targets: - get_cmd_output("cd rust-runtime/aws-smithy-http-server-python/examples && make build", shell=True, check=False) + get_cmd_output(f"./gradlew --rerun-tasks {clean_tasks}") + get_cmd_output(f"./gradlew --rerun-tasks {assemble_tasks}") # Move generated code into codegen-diff/ directory get_cmd_output(f"rm -rf {OUTPUT_PATH}") get_cmd_output(f"mkdir {OUTPUT_PATH}") - if 'aws:sdk' in targets: + if target_aws_sdk in targets: get_cmd_output(f"mv aws/sdk/build/aws-sdk {OUTPUT_PATH}/") - for target in ['codegen-client', 'codegen-server']: + for target in [target_codegen_client, target_codegen_server]: if target in targets: get_cmd_output(f"mv {target}/build/smithyprojections/{target} {OUTPUT_PATH}/") - if target == 'codegen-server-test': - get_cmd_output(f"mv rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-server-sdk/ {OUTPUT_PATH}/codegen-server-test-python/", check=False) + if target == target_codegen_server: + get_cmd_output(f"./gradlew --rerun-tasks {target_codegen_server_python}:stubs") + get_cmd_output(f"mv {target}/python/build/smithyprojections/{target}-python {OUTPUT_PATH}/") + get_cmd_output(f"mv {target}/typescript/build/smithyprojections/{target}-typescript {OUTPUT_PATH}/") # Clean up the SDK directory get_cmd_output(f"rm -f {OUTPUT_PATH}/aws-sdk/versions.toml") @@ -52,15 +76,23 @@ def generate_and_commit_generated_code(revision_sha, targets=None): # Clean up the server-test folder get_cmd_output(f"rm -rf {OUTPUT_PATH}/codegen-server-test/source") + get_cmd_output(f"rm -rf {OUTPUT_PATH}/codegen-server-test-python/source") + get_cmd_output(f"rm -rf {OUTPUT_PATH}/codegen-server-test-typescript/source") run(f"find {OUTPUT_PATH}/codegen-server-test | " f"grep -E 'smithy-build-info.json|sources/manifest|model.json' | " f"xargs rm -f", shell=True) + run(f"find {OUTPUT_PATH}/codegen-server-test-python | " + f"grep -E 'smithy-build-info.json|sources/manifest|model.json' | " + f"xargs rm -f", shell=True) + run(f"find {OUTPUT_PATH}/codegen-server-test-typescript | " + f"grep -E 'smithy-build-info.json|sources/manifest|model.json' | " + f"xargs rm -f", shell=True) get_cmd_output(f"git add -f {OUTPUT_PATH}") get_cmd_output(f"git -c 'user.name=GitHub Action (generated code preview)' " - f"-c 'user.name={COMMIT_AUTHOR_NAME}' " - f"-c 'user.email={COMMIT_AUTHOR_EMAIL}' " - f"commit --no-verify -m 'Generated code for {revision_sha}' --allow-empty") + f"-c 'user.name={COMMIT_AUTHOR_NAME}' " + f"-c 'user.email={COMMIT_AUTHOR_EMAIL}' " + f"commit --no-verify -m 'Generated code for {revision_sha}' --allow-empty") def make_diff(title, path_to_diff, base_commit_sha, head_commit_sha, suffix, whitespace): @@ -110,6 +142,10 @@ def make_diffs(base_commit_sha, head_commit_sha): head_commit_sha, "server-test-python", whitespace=True) server_nows_python = make_diff("Server Test Python", f"{OUTPUT_PATH}/codegen-server-test-python", base_commit_sha, head_commit_sha, "server-test-python-ignore-whitespace", whitespace=False) + server_ws_typescript = make_diff("Server Test Typescript", f"{OUTPUT_PATH}/codegen-server-test-typescript", base_commit_sha, + head_commit_sha, "server-test-typescript", whitespace=True) + server_nows_typescript = make_diff("Server Test Typescript", f"{OUTPUT_PATH}/codegen-server-test-typescript", base_commit_sha, + head_commit_sha, "server-test-typescript-ignore-whitespace", whitespace=False) sdk_links = diff_link('AWS SDK', 'No codegen difference in the AWS SDK', sdk_ws, 'ignoring whitespace', sdk_nows) @@ -119,12 +155,15 @@ def make_diffs(base_commit_sha, head_commit_sha): server_ws, 'ignoring whitespace', server_nows) server_links_python = diff_link('Server Test Python', 'No codegen difference in the Server Test Python', server_ws_python, 'ignoring whitespace', server_nows_python) + server_links_typescript = diff_link('Server Test Typescript', 'No codegen difference in the Server Test Typescript', + server_ws_typescript, 'ignoring whitespace', server_nows_typescript) # Save escaped newlines so that the GitHub Action script gets the whole message return "A new generated diff is ready to view.\\n" \ f"- {sdk_links}\\n" \ f"- {client_links}\\n" \ f"- {server_links}\\n" \ - f"- {server_links_python}\\n" + f"- {server_links_python}\\n" \ + f"- {server_links_typescript}\\n" def write_to_file(path, text): @@ -146,12 +185,14 @@ def run(command, shell=False, check=True): # Returns (status, stdout, stderr) from a shell command -def get_cmd_output(command, cwd=None, check=True, **kwargs): +def get_cmd_output(command, cwd=None, check=True, quiet=False, **kwargs): if isinstance(command, str): - eprint(f"running {command}") + if not quiet: + eprint(f"running {command}") command = shlex.split(command) else: - eprint(f"running {' '.join(command)}") + if not quiet: + eprint(f"running {' '.join(command)}") result = subprocess.run( command, @@ -167,6 +208,7 @@ def get_cmd_output(command, cwd=None, check=True, **kwargs): return result.returncode, stdout, stderr + # Runs a shell command and returns its exit status def get_cmd_status(command): return subprocess.run(command, capture_output=True, shell=True).returncode diff --git a/tools/ci-scripts/codegen-diff/semver-checks.py b/tools/ci-scripts/codegen-diff/semver-checks.py new file mode 100755 index 0000000000..24f6785c81 --- /dev/null +++ b/tools/ci-scripts/codegen-diff/semver-checks.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +import sys +import os +from diff_lib import get_cmd_output, get_cmd_status, eprint, running_in_docker_build, checkout_commit_and_generate, \ + OUTPUT_PATH + + +CURRENT_BRANCH = 'current' +BASE_BRANCH = 'base' +# This script runs `cargo semver-checks` against a previous version of codegen +def main(skip_generation=False): + if len(sys.argv) != 3: + eprint("Usage: semver-checks.py ") + sys.exit(1) + + repository_root = sys.argv[1] + base_commit_sha = sys.argv[2] + os.chdir(repository_root) + (_, head_commit_sha, _) = get_cmd_output("git rev-parse HEAD") + + # Make sure the working tree is clean + if get_cmd_status("git diff --quiet") != 0: + eprint("working tree is not clean. aborting") + sys.exit(1) + + if not skip_generation: + checkout_commit_and_generate(head_commit_sha, CURRENT_BRANCH, targets=['aws:sdk']) + checkout_commit_and_generate(base_commit_sha, BASE_BRANCH, targets=['aws:sdk']) + get_cmd_output(f'git checkout {CURRENT_BRANCH}') + sdk_directory = os.path.join(OUTPUT_PATH, 'aws-sdk', 'sdk') + os.chdir(sdk_directory) + + failed = False + deny_list = [ + # add crate names here to exclude them from the semver checks + ] + for path in os.listdir(): + eprint(f'checking {path}...', end='') + if path not in deny_list and get_cmd_status(f'git cat-file -e base:{sdk_directory}/{path}/Cargo.toml') == 0: + (status, out, err) = get_cmd_output(f'cargo semver-checks check-release ' + f'--baseline-rev {BASE_BRANCH} ' + # in order to get semver-checks to work with publish-false crates, need to specify + # package and manifest path explicitly + f'--manifest-path {path}/Cargo.toml ' + f'-p {path} ' + f'--release-type minor', check=False, quiet=True) + if status == 0: + eprint('ok!') + else: + failed = True + eprint('failed!') + if out: + eprint(out) + eprint(err) + else: + eprint(f'skipping {path} because it does not exist in base') + if failed: + eprint('One or more crates failed semver checks!') + exit(1) + + +if __name__ == "__main__": + skip_generation = bool(os.environ.get('SKIP_GENERATION') or False) + main(skip_generation=skip_generation) diff --git a/tools/ci-scripts/generate-aws-sdk b/tools/ci-scripts/generate-aws-sdk index 818e536126..18b29529ad 100755 --- a/tools/ci-scripts/generate-aws-sdk +++ b/tools/ci-scripts/generate-aws-sdk @@ -25,7 +25,8 @@ echo -e "${C_YELLOW}Taking examples from 'awsdocs/aws-doc-sdk-examples'...${C_RE examples_revision=$(cd aws-doc-sdk-examples; git rev-parse HEAD) mv aws-doc-sdk-examples/rust_dev_preview smithy-rs/aws/sdk/examples rm -rf smithy-rs/aws/sdk/examples/.cargo -rm smithy-rs/aws/sdk/examples/Cargo.toml +# TODO(https://github.com/awslabs/smithy-rs/issues/2810): This Cargo.toml `rm` can be removed when the flat example structure is cleaned up +rm -f smithy-rs/aws/sdk/examples/Cargo.toml echo -e "${C_YELLOW}Creating empty model metadata file since we don't have model update information...${C_RESET}" MODEL_METADATA_PATH="$(pwd)/model-metadata.toml" diff --git a/tools/echo-server/Cargo.lock b/tools/echo-server/Cargo.lock index 07ba693807..fd15209512 100644 --- a/tools/echo-server/Cargo.lock +++ b/tools/echo-server/Cargo.lock @@ -3,19 +3,34 @@ version = 3 [[package]] -name = "ansi_term" -version = "0.12.1" +name = "addr2line" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ - "winapi", + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", ] [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", @@ -30,9 +45,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" +checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" dependencies = [ "async-trait", "axum-core", @@ -61,9 +76,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" +checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" dependencies = [ "async-trait", "bytes", @@ -75,6 +90,21 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -83,9 +113,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -113,45 +149,45 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -159,11 +195,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.14" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -186,18 +228,15 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -217,9 +256,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" @@ -235,9 +274,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -259,9 +298,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -269,9 +308,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" @@ -281,15 +320,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -297,12 +336,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "matchers" @@ -310,7 +346,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -327,37 +363,70 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", "windows-sys", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.15.0" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" @@ -371,37 +440,37 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-targets", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", @@ -410,9 +479,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -422,38 +491,41 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "regex-syntax", + "aho-corasick", + "memchr", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", ] [[package]] @@ -462,38 +534,61 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", ] [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -523,33 +618,33 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -557,9 +652,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.101" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -568,29 +663,30 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] [[package]] name = "tokio" -version = "1.21.2" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -598,14 +694,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", @@ -614,9 +710,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -644,9 +740,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ "bitflags", "bytes", @@ -663,9 +759,9 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" @@ -675,9 +771,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", @@ -688,9 +784,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", @@ -699,9 +795,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -720,12 +816,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ - "ansi_term", "matchers", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", @@ -738,15 +834,15 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "valuable" @@ -756,11 +852,10 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -794,43 +889,66 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/tools/echo-server/Cargo.toml b/tools/echo-server/Cargo.toml index df50f243be..15331de6e8 100644 --- a/tools/echo-server/Cargo.toml +++ b/tools/echo-server/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" [dependencies] axum = "0.5.6" +hyper = { version = "0.14.26", features = ["full"] } tokio = { version = "1.20.1", features = ["full"] } +tower = { version = "0.4", features = ["util", "filter"] } tracing = "0.1" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } -tower = { version = "0.4", features = ["util", "filter"] } -hyper = { version = "0.14.25", features = ["full"] }